AI_ChemTutor / Pages /Items /Study_Material.txt
Abbasaabdul's picture
Upload 7 files
b3a7bda verified
import React, { useState, useEffect } from "react";
import { User } from "@/entities/User";
import { InvokeLLM } from "@/integrations/Core";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Badge } from "@/components/ui/badge";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Alert, AlertDescription } from "@/components/ui/alert";
import {
BookOpen,
FileText,
Lightbulb,
RefreshCw,
Download,
Sparkles,
GraduationCap,
Target,
Brain,
CheckCircle
} from "lucide-react";
const TOPICS = [
{ value: "organic_chemistry", label: "Organic Chemistry", icon: "🧪", color: "bg-green-100 text-green-800" },
{ value: "inorganic_chemistry", label: "Inorganic Chemistry", icon: "⚛️", color: "bg-blue-100 text-blue-800" },
{ value: "physical_chemistry", label: "Physical Chemistry", icon: "🔬", color: "bg-purple-100 text-purple-800" },
{ value: "analytical_chemistry", label: "Analytical Chemistry", icon: "📊", color: "bg-orange-100 text-orange-800" },
{ value: "biochemistry", label: "Biochemistry", icon: "🧬", color: "bg-pink-100 text-pink-800" },
{ value: "quantum_chemistry", label: "Quantum Chemistry", icon: "⚡", color: "bg-indigo-100 text-indigo-800" },
{ value: "materials_science", label: "Materials Science", icon: "🏗️", color: "bg-cyan-100 text-cyan-800" }
];
const MATERIAL_TYPES = [
{ value: "study_guide", label: "Study Guide", icon: BookOpen, description: "Comprehensive topic overview" },
{ value: "flashcards", label: "Flashcards", icon: Brain, description: "Key terms and definitions" },
{ value: "practice_problems", label: "Practice Problems", icon: Target, description: "Problems with solutions" },
{ value: "concept_map", label: "Concept Map", icon: Lightbulb, description: "Visual concept connections" }
];
export default function StudyMaterials() {
const [user, setUser] = useState(null);
const [selectedTopic, setSelectedTopic] = useState("");
const [selectedType, setSelectedType] = useState("");
const [generatedMaterial, setGeneratedMaterial] = useState(null);
const [isGenerating, setIsGenerating] = useState(false);
useEffect(() => {
loadUser();
}, []);
const loadUser = async () => {
try {
const userData = await User.me();
setUser(userData);
} catch (error) {
await User.login();
}
};
const generateMaterial = async () => {
if (!selectedTopic || !selectedType) return;
setIsGenerating(true);
setGeneratedMaterial(null);
try {
const topic = TOPICS.find(t => t.value === selectedTopic);
const materialType = MATERIAL_TYPES.find(t => t.value === selectedType);
let prompt = "";
let schema = {};
switch (selectedType) {
case "study_guide":
prompt = `Create a comprehensive study guide for ${topic.label} suitable for a ${user?.learning_level || 'beginner'} level student. Include:
- Overview and importance
- Key concepts with clear explanations
- Important formulas or reactions
- Real-world applications
- Common misconceptions
- Study tips and memory aids`;
schema = {
type: "object",
properties: {
title: { type: "string" },
overview: { type: "string" },
key_concepts: {
type: "array",
items: {
type: "object",
properties: {
concept: { type: "string" },
explanation: { type: "string" }
}
}
},
formulas: { type: "array", items: { type: "string" } },
applications: { type: "array", items: { type: "string" } },
misconceptions: { type: "array", items: { type: "string" } },
study_tips: { type: "array", items: { type: "string" } }
}
};
break;
case "flashcards":
prompt = `Create 15-20 flashcards for ${topic.label} at ${user?.learning_level || 'beginner'} level. Each flashcard should have:
- A clear, concise question or term
- A comprehensive answer or definition
- Include key formulas, reactions, or concepts`;
schema = {
type: "object",
properties: {
title: { type: "string" },
cards: {
type: "array",
items: {
type: "object",
properties: {
front: { type: "string" },
back: { type: "string" }
}
}
}
}
};
break;
case "practice_problems":
prompt = `Create 8-10 practice problems for ${topic.label} at ${user?.learning_level || 'beginner'} level. Include:
- Varied difficulty within the level
- Step-by-step solutions
- Brief explanations of key concepts used`;
schema = {
type: "object",
properties: {
title: { type: "string" },
problems: {
type: "array",
items: {
type: "object",
properties: {
question: { type: "string" },
solution: { type: "string" },
concepts_used: { type: "array", items: { type: "string" } }
}
}
}
}
};
break;
case "concept_map":
prompt = `Create a concept map structure for ${topic.label} at ${user?.learning_level || 'beginner'} level showing:
- Main concepts and their relationships
- Hierarchical organization
- Key connections between ideas
- Brief descriptions for each concept`;
schema = {
type: "object",
properties: {
title: { type: "string" },
main_concept: { type: "string" },
concepts: {
type: "array",
items: {
type: "object",
properties: {
name: { type: "string" },
description: { type: "string" },
level: { type: "number" },
connections: { type: "array", items: { type: "string" } }
}
}
}
}
};
break;
}
const result = await InvokeLLM({
prompt,
response_json_schema: schema
});
setGeneratedMaterial({
type: selectedType,
topic: selectedTopic,
data: result
});
} catch (error) {
console.error("Error generating material:", error);
}
setIsGenerating(false);
};
const downloadMaterial = () => {
if (!generatedMaterial) return;
let content = "";
const { data } = generatedMaterial;
switch (generatedMaterial.type) {
case "study_guide":
content = `${data.title}\n\n`;
content += `Overview:\n${data.overview}\n\n`;
if (data.key_concepts) {
content += "Key Concepts:\n";
data.key_concepts.forEach(concept => {
content += `- ${concept.concept}: ${concept.explanation}\n`;
});
content += "\n";
}
if (data.formulas && data.formulas.length > 0) {
content += "Important Formulas/Reactions:\n";
data.formulas.forEach(formula => {
content += `- ${formula}\n`;
});
content += "\n";
}
if (data.applications && data.applications.length > 0) {
content += "Real-world Applications:\n";
data.applications.forEach(app => {
content += `- ${app}\n`;
});
content += "\n";
}
if (data.misconceptions && data.misconceptions.length > 0) {
content += "Common Misconceptions:\n";
data.misconceptions.forEach(misconception => {
content += `- ${misconception}\n`;
});
content += "\n";
}
if (data.study_tips && data.study_tips.length > 0) {
content += "Study Tips:\n";
data.study_tips.forEach(tip => {
content += `- ${tip}\n`;
});
content += "\n";
}
break;
case "flashcards":
content = `${data.title}\n\nFlashcards:\n\n`;
data.cards?.forEach((card, index) => {
content += `Card ${index + 1}:\nQ: ${card.front}\nA: ${card.back}\n\n`;
});
break;
case "practice_problems":
content = `${data.title}\n\nPractice Problems:\n\n`;
data.problems?.forEach((problem, index) => {
content += `Problem ${index + 1}:\nQuestion: ${problem.question}\nSolution: ${problem.solution}\nConcepts Used: ${problem.concepts_used?.join(', ')}\n\n`;
});
break;
case "concept_map":
content = `${data.title}\n\nMain Concept: ${data.main_concept}\n\n`;
content += "Concepts:\n";
data.concepts?.forEach(concept => {
content += `- Name: ${concept.name}\n Description: ${concept.description}\n Level: ${concept.level}\n Connections: ${concept.connections?.join(', ')}\n\n`;
});
break;
}
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${data.title.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_.]/g, '')}.txt`; // Sanitize filename
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
const renderMaterial = () => {
if (!generatedMaterial) return null;
const { data, type } = generatedMaterial;
switch (type) {
case "study_guide":
return (
<div className="space-y-6">
<Card className="bg-gradient-to-br from-blue-50 to-indigo-50 border-blue-200">
<CardHeader>
<CardTitle className="text-2xl">{data.title}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-lg text-slate-700 leading-relaxed">{data.overview}</p>
</CardContent>
</Card>
{data.key_concepts && data.key_concepts.length > 0 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Lightbulb className="w-5 h-5 text-amber-500" />
Key Concepts
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{data.key_concepts.map((concept, index) => (
<div key={index} className="p-4 bg-slate-50 rounded-lg">
<h4 className="font-semibold text-slate-900 mb-2">{concept.concept}</h4>
<p className="text-slate-700">{concept.explanation}</p>
</div>
))}
</div>
</CardContent>
</Card>
)}
{data.formulas && data.formulas.length > 0 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<FileText className="w-5 h-5 text-red-500" />
Important Formulas/Reactions
</CardTitle>
</CardHeader>
<CardContent>
<ul className="list-disc list-inside space-y-2">
{data.formulas.map((formula, index) => (
<li key={index} className="text-slate-700">{formula}</li>
))}
</ul>
</CardContent>
</Card>
)}
{data.applications && data.applications.length > 0 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Target className="w-5 h-5 text-orange-500" />
Real-world Applications
</CardTitle>
</CardHeader>
<CardContent>
<ul className="list-disc list-inside space-y-2">
{data.applications.map((app, index) => (
<li key={index} className="text-slate-700">{app}</li>
))}
</ul>
</CardContent>
</Card>
)}
{data.misconceptions && data.misconceptions.length > 0 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Lightbulb className="w-5 h-5 text-purple-500" />
Common Misconceptions
</CardTitle>
</CardHeader>
<CardContent>
<ul className="list-disc list-inside space-y-2">
{data.misconceptions.map((misconception, index) => (
<li key={index} className="text-slate-700">{misconception}</li>
))}
</ul>
</CardContent>
</Card>
)}
{data.study_tips && data.study_tips.length > 0 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<GraduationCap className="w-5 h-5 text-emerald-500" />
Study Tips
</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-2">
{data.study_tips.map((tip, index) => (
<li key={index} className="flex items-start gap-3">
<CheckCircle className="w-5 h-5 text-emerald-500 mt-0.5 flex-shrink-0" />
<span className="text-slate-700">{tip}</span>
</li>
))}
</ul>
</CardContent>
</Card>
)}
</div>
);
case "flashcards":
return (
<div className="space-y-6">
<Card className="bg-gradient-to-br from-purple-50 to-pink-50 border-purple-200">
<CardHeader>
<CardTitle className="text-2xl">{data.title}</CardTitle>
<p className="text-slate-600">{data.cards?.length} flashcards</p>
</CardHeader>
</Card>
<div className="grid md:grid-cols-2 gap-4">
{data.cards?.map((card, index) => (
<Card key={index} className="hover:shadow-lg transition-shadow">
<CardContent className="p-6">
<div className="text-center">
<Badge className="mb-4">Card {index + 1}</Badge>
<div className="space-y-4">
<div>
<h4 className="font-medium text-slate-600 mb-2">Question:</h4>
<p className="font-semibold text-slate-900">{card.front}</p>
</div>
<div className="border-t pt-4">
<h4 className="font-medium text-slate-600 mb-2">Answer:</h4>
<p className="text-slate-800">{card.back}</p>
</div>
</div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
);
case "practice_problems":
return (
<div className="space-y-6">
<Card className="bg-gradient-to-br from-teal-50 to-emerald-50 border-teal-200">
<CardHeader>
<CardTitle className="text-2xl">{data.title}</CardTitle>
<p className="text-slate-600">{data.problems?.length} practice problems</p>
</CardHeader>
</Card>
<div className="space-y-4">
{data.problems?.map((problem, index) => (
<Card key={index} className="hover:shadow-lg transition-shadow">
<CardContent className="p-6">
<div className="space-y-4">
<div>
<h4 className="font-medium text-slate-600 mb-2">Problem {index + 1}:</h4>
<p className="font-semibold text-slate-900">{problem.question}</p>
</div>
<div className="border-t pt-4">
<h4 className="font-medium text-slate-600 mb-2">Solution:</h4>
<p className="text-slate-800 whitespace-pre-wrap">{problem.solution}</p>
</div>
{problem.concepts_used && problem.concepts_used.length > 0 && (
<div className="border-t pt-4">
<h4 className="font-medium text-slate-600 mb-2">Concepts Used:</h4>
<div className="flex flex-wrap gap-2">
{problem.concepts_used.map((concept, idx) => (
<Badge key={idx} variant="outline" className="bg-blue-50 text-blue-700">{concept}</Badge>
))}
</div>
</div>
)}
</div>
</CardContent>
</Card>
))}
</div>
</div>
);
case "concept_map":
return (
<div className="space-y-6">
<Card className="bg-gradient-to-br from-yellow-50 to-orange-50 border-yellow-200">
<CardHeader>
<CardTitle className="text-2xl">{data.title}</CardTitle>
<p className="text-slate-600">Main Concept: <span className="font-medium">{data.main_concept}</span></p>
</CardHeader>
</Card>
{data.concepts && data.concepts.length > 0 && (
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Lightbulb className="w-5 h-5 text-amber-500" />
Related Concepts
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
{data.concepts.map((concept, index) => (
<Card key={index} className="p-4 bg-slate-50 rounded-lg shadow-sm">
<h4 className="font-semibold text-slate-900 mb-2">{concept.name}</h4>
<p className="text-sm text-slate-700 mb-2">{concept.description}</p>
{concept.connections && concept.connections.length > 0 && (
<div className="mt-2">
<span className="font-medium text-xs text-slate-600">Connections: </span>
<div className="flex flex-wrap gap-1">
{concept.connections.map((conn, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">{conn}</Badge>
))}
</div>
</div>
)}
</Card>
))}
</div>
</CardContent>
</Card>
)}
</div>
);
default:
return (
<Alert>
<AlertDescription>
Material type not yet implemented for display.
</AlertDescription>
</Alert>
);
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-emerald-50 p-4 md:p-8">
<div className="max-w-6xl mx-auto">
<div className="mb-8">
<div className="flex items-center gap-3 mb-4">
<div className="w-12 h-12 bg-gradient-to-br from-emerald-600 to-teal-700 rounded-xl flex items-center justify-center">
<BookOpen className="w-6 h-6 text-white" />
</div>
<div>
<h1 className="text-3xl font-bold text-slate-900">AI Study Materials</h1>
<p className="text-slate-600">Generate personalized learning resources with AI</p>
</div>
</div>
</div>
{/* Material Generator */}
<Card className="border-0 shadow-xl bg-white/90 backdrop-blur-sm mb-8">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Sparkles className="w-5 h-5 text-amber-500" />
Generate Study Material
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid md:grid-cols-2 gap-6 mb-6">
<div>
<label className="block text-sm font-medium text-slate-700 mb-3">Chemistry Topic</label>
<div className="grid grid-cols-2 gap-2">
{TOPICS.map((topic) => (
<button
key={topic.value}
onClick={() => setSelectedTopic(topic.value)}
className={`p-3 text-left rounded-lg border transition-all duration-200 ${
selectedTopic === topic.value
? 'border-emerald-500 bg-emerald-50'
: 'border-slate-200 hover:border-emerald-300'
}`}
>
<div className="text-lg mb-1">{topic.icon}</div>
<div className="text-sm font-medium text-slate-900">{topic.label}</div>
</button>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-slate-700 mb-3">Material Type</label>
<div className="space-y-2">
{MATERIAL_TYPES.map((type) => (
<button
key={type.value}
onClick={() => setSelectedType(type.value)}
className={`w-full p-4 text-left rounded-lg border transition-all duration-200 ${
selectedType === type.value
? 'border-emerald-500 bg-emerald-50'
: 'border-slate-200 hover:border-emerald-300'
}`}
>
<div className="flex items-center gap-3">
<type.icon className="w-5 h-5 text-emerald-600" />
<div>
<div className="font-medium text-slate-900">{type.label}</div>
<div className="text-sm text-slate-500">{type.description}</div>
</div>
</div>
</button>
))}
</div>
</div>
</div>
<Button
onClick={generateMaterial}
disabled={!selectedTopic || !selectedType || isGenerating}
className="w-full bg-gradient-to-r from-emerald-600 to-teal-700 hover:from-emerald-700 hover:to-teal-800 font-semibold py-3"
>
{isGenerating ? (
<>
<RefreshCw className="w-5 h-5 mr-2 animate-spin" />
Generating Material...
</>
) : (
<>
<Brain className="w-5 h-5 mr-2" />
Generate Study Material
</>
)}
</Button>
</CardContent>
</Card>
{/* Generated Material */}
{generatedMaterial && (
<Card className="border-0 shadow-xl bg-white/90 backdrop-blur-sm">
<CardHeader>
<div className="flex items-center justify-between flex-wrap gap-4">
<div>
<CardTitle className="text-xl">Generated Material</CardTitle>
<div className="flex gap-2 mt-2">
<Badge className={TOPICS.find(t => t.value === generatedMaterial.topic)?.color}>
{TOPICS.find(t => t.value === generatedMaterial.topic)?.label}
</Badge>
<Badge variant="outline">
{MATERIAL_TYPES.find(t => t.value === generatedMaterial.type)?.label}
</Badge>
</div>
</div>
<Button onClick={downloadMaterial} variant="outline">
<Download className="w-4 h-4 mr-2" />
Download
</Button>
</div>
</CardHeader>
<CardContent>
{renderMaterial()}
</CardContent>
</Card>
)}
{/* Empty State */}
{!generatedMaterial && !isGenerating && (
<Card className="border-0 shadow-xl bg-white/80 backdrop-blur-sm">
<CardContent className="text-center py-12">
<BookOpen className="w-16 h-16 mx-auto text-slate-400 mb-4" />
<h3 className="text-xl font-semibold text-slate-900 mb-2">Ready to Study?</h3>
<p className="text-slate-600 mb-6">
Select a chemistry topic and material type above to generate personalized study materials!
</p>
</CardContent>
</Card>
)}
</div>
</div>
);
}