first commit

This commit is contained in:
inubimambo
2025-07-03 00:17:34 +08:00
commit d2653178e9
3 changed files with 2187 additions and 0 deletions

643
app.js Normal file
View File

@@ -0,0 +1,643 @@
// EduCat: AI Study Buddy - Main Application Logic
class EduCatApp {
constructor() {
this.uploadedFiles = [];
this.studySessions = 0;
this.quizzesCompleted = 0;
this.generatedContent = [];
this.currentTab = 'dashboard';
this.init();
}
init() {
this.setupEventListeners();
this.updateStats();
this.updateFileSelects();
this.showTab('dashboard');
}
setupEventListeners() {
// Tab navigation
document.querySelectorAll('.nav__item').forEach(button => {
button.addEventListener('click', (e) => {
const tab = e.target.dataset.tab || 'dashboard';
this.showTab(tab);
});
});
// File upload - Fixed implementation
const uploadArea = document.getElementById('upload-area');
const fileInput = document.getElementById('file-input');
if (uploadArea && fileInput) {
uploadArea.addEventListener('click', (e) => {
e.preventDefault();
fileInput.click();
});
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('drag-over');
});
uploadArea.addEventListener('dragleave', (e) => {
e.preventDefault();
uploadArea.classList.remove('drag-over');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('drag-over');
const files = Array.from(e.dataTransfer.files);
this.processFiles(files);
});
fileInput.addEventListener('change', (e) => {
const files = Array.from(e.target.files);
this.processFiles(files);
});
}
// AI feature buttons
document.querySelectorAll('.feature-card').forEach(card => {
const button = card.querySelector('.btn');
if (button) {
button.addEventListener('click', (e) => {
e.preventDefault();
const feature = card.dataset.feature;
this.generateContent(feature);
});
}
});
// Quiz generation
const generateQuizBtn = document.getElementById('generate-quiz');
if (generateQuizBtn) {
generateQuizBtn.addEventListener('click', (e) => {
e.preventDefault();
this.generateQuiz();
});
}
// Study planner interactions - Fixed implementation
this.setupPlannerEvents();
}
showTab(tabName) {
// Update navigation
document.querySelectorAll('.nav__item').forEach(item => {
item.classList.remove('active');
if (item.dataset.tab === tabName || (tabName === 'dashboard' && !item.dataset.tab)) {
item.classList.add('active');
}
});
// Update content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
const targetTab = tabName === 'dashboard' ? 'dashboard' : tabName;
const targetContent = document.getElementById(targetTab);
if (targetContent) {
targetContent.classList.add('active');
}
this.currentTab = tabName;
// Special handling for library tab
if (tabName === 'library') {
this.updateLibraryContent();
}
}
processFiles(files) {
if (!files || files.length === 0) {
alert('No files selected.');
return;
}
const validFiles = files.filter(file => this.isValidFile(file));
if (validFiles.length === 0) {
alert('Please select valid file types: PDF, DOCX, PPTX, TXT, or XLSX');
return;
}
if (validFiles.length !== files.length) {
alert('Some files were skipped due to invalid file types.');
}
this.uploadFiles(validFiles);
}
isValidFile(file) {
const validExtensions = ['pdf', 'docx', 'pptx', 'txt', 'xlsx'];
const fileName = file.name.toLowerCase();
return validExtensions.some(ext => fileName.endsWith('.' + ext));
}
uploadFiles(files) {
const progressBar = document.getElementById('upload-progress');
const progressFill = document.getElementById('progress-fill');
const progressText = document.getElementById('progress-text');
if (progressBar) {
progressBar.classList.remove('hidden');
}
let progress = 0;
const progressInterval = setInterval(() => {
progress += Math.random() * 20 + 5;
if (progressFill) {
progressFill.style.width = `${Math.min(progress, 100)}%`;
}
if (progressText) {
progressText.textContent = `Uploading... ${Math.round(Math.min(progress, 100))}%`;
}
if (progress >= 100) {
clearInterval(progressInterval);
this.completeUpload(files);
}
}, 300);
}
completeUpload(files) {
setTimeout(() => {
files.forEach(file => {
const fileData = {
id: Date.now() + Math.random(),
name: file.name,
size: this.formatFileSize(file.size || 50000), // Default size for demo
type: this.getFileType(file.name),
uploadDate: new Date().toLocaleDateString(),
content: this.generateMockContent(file.name)
};
this.uploadedFiles.push(fileData);
});
const progressBar = document.getElementById('upload-progress');
if (progressBar) {
progressBar.classList.add('hidden');
}
this.updateFileDisplay();
this.updateStats();
this.updateFileSelects();
this.addActivity(`Uploaded ${files.length} file(s)`);
// Show success message
alert(`Successfully uploaded ${files.length} file(s)!`);
// Clear file input
const fileInput = document.getElementById('file-input');
if (fileInput) {
fileInput.value = '';
}
}, 500);
}
getFileType(filename) {
const extension = filename.split('.').pop().toUpperCase();
return extension;
}
formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
generateMockContent(filename) {
const topics = ['Mathematics', 'Science', 'History', 'Literature', 'Computer Science'];
const randomTopic = topics[Math.floor(Math.random() * topics.length)];
return {
topic: randomTopic,
keyPoints: [
`Key concept from ${filename}`,
`Important theorem or principle`,
`Practical application example`,
`Historical context or background`
]
};
}
updateFileDisplay() {
const container = document.getElementById('files-container');
if (!container) return;
if (this.uploadedFiles.length === 0) {
container.innerHTML = '<div class="empty-state"><p>No files uploaded yet. Upload your first file to get started!</p></div>';
return;
}
container.innerHTML = this.uploadedFiles.map(file => `
<div class="file-item" data-file-id="${file.id}">
<div class="file-icon">${file.type}</div>
<div class="file-info">
<div class="file-name">${file.name}</div>
<div class="file-meta">${file.size}${file.uploadDate}</div>
</div>
</div>
`).join('');
}
updateStats() {
const filesCount = document.getElementById('files-count');
const sessionsCount = document.getElementById('sessions-count');
const quizzesCount = document.getElementById('quizzes-count');
if (filesCount) filesCount.textContent = this.uploadedFiles.length;
if (sessionsCount) sessionsCount.textContent = this.studySessions;
if (quizzesCount) quizzesCount.textContent = this.quizzesCompleted;
}
updateFileSelects() {
const select = document.getElementById('quiz-file-select');
if (!select) return;
select.innerHTML = '<option value="">Select a file to create quiz from</option>';
this.uploadedFiles.forEach(file => {
const option = document.createElement('option');
option.value = file.id;
option.textContent = file.name;
select.appendChild(option);
});
}
addActivity(activity) {
const activityList = document.getElementById('activity-list');
if (!activityList) return;
const timestamp = new Date().toLocaleTimeString();
// Remove empty state if it exists
const emptyState = activityList.querySelector('.empty-state');
if (emptyState) {
emptyState.remove();
}
const activityItem = document.createElement('div');
activityItem.className = 'activity-item';
activityItem.innerHTML = `
<span>📝</span>
<span>${activity}</span>
<small>${timestamp}</small>
`;
activityList.insertBefore(activityItem, activityList.firstChild);
// Keep only last 5 activities
const items = activityList.querySelectorAll('.activity-item');
if (items.length > 5) {
items[items.length - 1].remove();
}
}
generateContent(feature) {
if (this.uploadedFiles.length === 0) {
alert('Please upload some files first before generating content.');
return;
}
this.showLoadingModal(`Generating ${feature.replace('-', ' ')}...`);
setTimeout(() => {
const content = this.createMockContent(feature);
this.generatedContent.push(content);
this.hideLoadingModal();
this.updateLibraryContent();
this.addActivity(`Generated ${feature.replace('-', ' ')}`);
// Switch to library tab to show the generated content
this.showTab('library');
}, 2000 + Math.random() * 2000);
}
createMockContent(feature) {
const randomFile = this.uploadedFiles[Math.floor(Math.random() * this.uploadedFiles.length)];
const timestamp = new Date().toLocaleString();
const contentMap = {
'study-guide': {
title: `Study Guide: ${randomFile.content.topic}`,
type: 'Study Guide',
content: `
<h4>Chapter Overview</h4>
<p>This study guide covers the key concepts from ${randomFile.name}.</p>
<h4>Key Topics</h4>
<ul>
${randomFile.content.keyPoints.map(point => `<li>${point}</li>`).join('')}
</ul>
<h4>Study Tips</h4>
<ul>
<li>Review each section multiple times</li>
<li>Create flashcards for key terms</li>
<li>Practice with sample problems</li>
<li>Form study groups for discussion</li>
</ul>
`
},
'quiz': {
title: `Practice Quiz: ${randomFile.content.topic}`,
type: 'Quiz',
content: `
<div class="quiz-questions">
<div class="quiz-question">
<div class="question-text">1. What is the main concept discussed in the uploaded material?</div>
<div class="quiz-options-list">
<label class="quiz-option">
<input type="radio" name="q1" value="a">
<span>${randomFile.content.keyPoints[0]}</span>
</label>
<label class="quiz-option">
<input type="radio" name="q1" value="b">
<span>Alternative concept</span>
</label>
</div>
</div>
</div>
`
},
'summary': {
title: `Summary: ${randomFile.content.topic}`,
type: 'Summary',
content: `
<p><strong>Document:</strong> ${randomFile.name}</p>
<p><strong>Topic:</strong> ${randomFile.content.topic}</p>
<h4>Key Points Summary</h4>
<p>The main concepts covered include:</p>
<ul>
${randomFile.content.keyPoints.map(point => `<li>${point}</li>`).join('')}
</ul>
<p>This material provides a comprehensive overview of ${randomFile.content.topic} with practical applications and theoretical foundations.</p>
`
},
'flashcards': {
title: `Flashcards: ${randomFile.content.topic}`,
type: 'Flashcards',
content: `
<div class="flashcard-set">
${randomFile.content.keyPoints.map((point, index) => `
<div class="flashcard" style="margin-bottom: 16px; padding: 16px; border: 1px solid var(--color-border); border-radius: 8px;">
<div class="flashcard-front"><strong>Card ${index + 1}:</strong> What is ${point.toLowerCase()}?</div>
<div class="flashcard-back" style="margin-top: 8px; color: var(--color-text-secondary);">Answer: ${point}</div>
</div>
`).join('')}
</div>
`
}
};
return {
id: Date.now() + Math.random(),
...contentMap[feature],
sourceFile: randomFile.name,
createdAt: timestamp
};
}
updateLibraryContent() {
const container = document.getElementById('generated-content');
if (!container) return;
if (this.generatedContent.length === 0) {
container.style.display = 'none';
return;
}
container.style.display = 'block';
container.innerHTML = `
<h3>Generated Study Materials</h3>
${this.generatedContent.map(content => `
<div class="content-item">
<div class="content-header">
<span class="content-title">${content.title}</span>
<span class="status status--success">${content.type}</span>
</div>
<div class="content-body">
<p><small>Generated from: ${content.sourceFile}${content.createdAt}</small></p>
${content.content}
</div>
</div>
`).join('')}
`;
}
generateQuiz() {
const fileSelect = document.getElementById('quiz-file-select');
const questionsSelect = document.getElementById('quiz-questions');
if (!fileSelect || !fileSelect.value) {
alert('Please select a file to generate quiz from.');
return;
}
const selectedFile = this.uploadedFiles.find(file => file.id == fileSelect.value);
const numQuestions = parseInt(questionsSelect.value) || 10;
this.showLoadingModal('Generating quiz questions...');
setTimeout(() => {
const quiz = this.createQuizQuestions(selectedFile, numQuestions);
this.displayQuiz(quiz);
this.hideLoadingModal();
this.addActivity(`Generated ${numQuestions}-question quiz`);
}, 1500 + Math.random() * 1500);
}
createQuizQuestions(file, numQuestions) {
const questions = [];
const questionTypes = [
'What is the main concept of',
'Which of the following best describes',
'What is the key principle behind',
'How does this concept apply to',
'What is the relationship between'
];
for (let i = 0; i < numQuestions; i++) {
const questionType = questionTypes[Math.floor(Math.random() * questionTypes.length)];
const keyPoint = file.content.keyPoints[i % file.content.keyPoints.length];
questions.push({
id: i + 1,
question: `${questionType} ${keyPoint.toLowerCase()}?`,
options: [
keyPoint,
'Alternative option A',
'Alternative option B',
'Alternative option C'
].sort(() => Math.random() - 0.5),
correct: keyPoint
});
}
return {
title: `Quiz: ${file.content.topic}`,
source: file.name,
questions: questions
};
}
displayQuiz(quiz) {
const container = document.getElementById('quiz-container');
if (!container) return;
container.classList.remove('hidden');
container.innerHTML = `
<div class="quiz-header">
<h3>${quiz.title}</h3>
<p>Source: ${quiz.source}</p>
</div>
<div class="quiz-questions">
${quiz.questions.map(q => `
<div class="quiz-question">
<div class="question-text">${q.id}. ${q.question}</div>
<div class="quiz-options-list">
${q.options.map((option, index) => `
<label class="quiz-option">
<input type="radio" name="q${q.id}" value="${option}">
<span>${option}</span>
</label>
`).join('')}
</div>
</div>
`).join('')}
</div>
<button class="btn btn--primary" onclick="app.submitQuiz()">Submit Quiz</button>
`;
}
submitQuiz() {
const questions = document.querySelectorAll('.quiz-question');
let score = 0;
let total = questions.length;
questions.forEach((question, index) => {
const selected = question.querySelector('input[type="radio"]:checked');
if (selected) {
// For demo purposes, we'll give a random score
if (Math.random() > 0.3) score++;
}
});
this.quizzesCompleted++;
this.updateStats();
this.addActivity(`Completed quiz - Score: ${score}/${total}`);
alert(`Quiz completed! Your score: ${score}/${total} (${Math.round((score/total) * 100)}%)`);
}
setupPlannerEvents() {
// Wait for DOM to be ready and then attach events
setTimeout(() => {
const plannerButtons = document.querySelectorAll('.planner-card .btn--secondary');
plannerButtons.forEach((btn, index) => {
// Remove existing listeners to prevent duplication
btn.replaceWith(btn.cloneNode(true));
const newBtn = document.querySelectorAll('.planner-card .btn--secondary')[index];
newBtn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
if (index === 0) {
this.addStudySession();
} else if (index === 1) {
this.addStudyGoal();
} else if (index === 2) {
this.setReminder();
}
});
});
// Goal checkboxes
const goalCheckboxes = document.querySelectorAll('.goal-item input[type="checkbox"]');
goalCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
this.studySessions++;
this.updateStats();
this.addActivity('Completed study goal');
}
});
});
}, 100);
}
addStudySession() {
const time = prompt('Enter study time (e.g., 3:00 PM):');
const subject = prompt('Enter subject:');
if (time && subject) {
this.studySessions++;
this.updateStats();
this.addActivity(`Scheduled: ${subject} at ${time}`);
alert('Study session added to your schedule!');
}
}
addStudyGoal() {
const goal = prompt('Enter your study goal:');
if (goal) {
this.addActivity(`New goal: ${goal}`);
alert('Study goal added!');
}
}
setReminder() {
const reminder = prompt('Enter reminder text:');
if (reminder) {
this.addActivity(`Set reminder: ${reminder}`);
alert('Reminder set!');
}
}
showLoadingModal(title, text = 'Please wait while we analyze your content and generate study materials.') {
const modal = document.getElementById('loading-modal');
const titleEl = document.getElementById('loading-title');
const textEl = document.getElementById('loading-text');
if (modal) modal.classList.remove('hidden');
if (titleEl) titleEl.textContent = title;
if (textEl) textEl.textContent = text;
}
hideLoadingModal() {
const modal = document.getElementById('loading-modal');
if (modal) modal.classList.add('hidden');
}
}
// Initialize the application
let app;
document.addEventListener('DOMContentLoaded', () => {
app = new EduCatApp();
// Add some sample activity for demo
setTimeout(() => {
app.addActivity('Welcome to EduCat!');
}, 1000);
});
// Handle modal click to close
document.addEventListener('click', (e) => {
if (e.target.id === 'loading-modal') {
if (app) app.hideLoadingModal();
}
});

247
index.html Normal file
View File

@@ -0,0 +1,247 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EduCat: AI Study Buddy</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header class="header">
<div class="container">
<div class="header__content">
<div class="logo">
<h1 class="logo__text">🐱 EduCat</h1>
<span class="logo__subtitle">AI Study Buddy</span>
</div>
<nav class="nav">
<button class="nav__item" data-tab="upload">Upload Files</button>
<button class="nav__item" data-tab="library">My Notes</button>
<button class="nav__item" data-tab="quiz">Quiz Generator</button>
<button class="nav__item" data-tab="planner">Study Planner</button>
</nav>
</div>
<div class="welcome-message">
<p>Welcome to EduCat - Your AI Study Buddy!</p>
</div>
</div>
</header>
<main class="main">
<div class="container">
<!-- Dashboard Tab -->
<section id="dashboard" class="tab-content active">
<div class="hero">
<h2>Transform Your Study Materials with AI</h2>
<p>Upload your notes, textbooks, and study materials. Let our AI create personalized study guides, quizzes, summaries, and flashcards to boost your learning efficiency.</p>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-card__icon">📁</div>
<div class="stat-card__content">
<span class="stat-card__number" id="files-count">0</span>
<span class="stat-card__label">Files Uploaded</span>
</div>
</div>
<div class="stat-card">
<div class="stat-card__icon">📚</div>
<div class="stat-card__content">
<span class="stat-card__number" id="sessions-count">0</span>
<span class="stat-card__label">Study Sessions</span>
</div>
</div>
<div class="stat-card">
<div class="stat-card__icon">🧠</div>
<div class="stat-card__content">
<span class="stat-card__number" id="quizzes-count">0</span>
<span class="stat-card__label">Quizzes Completed</span>
</div>
</div>
</div>
<div class="recent-activity">
<h3>Recent Activity</h3>
<div id="activity-list" class="activity-list">
<div class="activity-item empty-state">
<p>No recent activity. Start by uploading your first file!</p>
</div>
</div>
</div>
</section>
<!-- Upload Tab -->
<section id="upload" class="tab-content">
<div class="upload-section">
<h2>Upload Your Study Materials</h2>
<div class="upload-area" id="upload-area">
<div class="upload-area__content">
<div class="upload-area__icon">📤</div>
<h3>Drag & Drop Files Here</h3>
<p>or click to browse files</p>
<input type="file" id="file-input" multiple accept=".pdf,.docx,.pptx,.txt,.xlsx">
</div>
<div class="upload-area__formats">
<span class="format-badge">PDF</span>
<span class="format-badge">DOCX</span>
<span class="format-badge">PPTX</span>
<span class="format-badge">TXT</span>
<span class="format-badge">XLSX</span>
</div>
</div>
<div id="upload-progress" class="upload-progress hidden">
<div class="progress-bar">
<div class="progress-fill" id="progress-fill"></div>
</div>
<span id="progress-text">Uploading...</span>
</div>
<div id="file-list" class="file-list">
<h3>Uploaded Files</h3>
<div id="files-container" class="files-container">
<div class="empty-state">
<p>No files uploaded yet. Upload your first file to get started!</p>
</div>
</div>
</div>
</div>
</section>
<!-- Library Tab -->
<section id="library" class="tab-content">
<div class="library-section">
<h2>My Study Library</h2>
<div class="ai-features">
<div class="feature-card" data-feature="study-guide">
<div class="feature-card__icon">📖</div>
<h3>Study Guide Generator</h3>
<p>Convert your files into structured study guides</p>
<button class="btn btn--primary">Generate Study Guide</button>
</div>
<div class="feature-card" data-feature="quiz">
<div class="feature-card__icon"></div>
<h3>Quiz Creator</h3>
<p>Generate practice questions from your content</p>
<button class="btn btn--primary">Create Quiz</button>
</div>
<div class="feature-card" data-feature="summary">
<div class="feature-card__icon">📝</div>
<h3>Summary Generator</h3>
<p>Create concise summaries of long documents</p>
<button class="btn btn--primary">Generate Summary</button>
</div>
<div class="feature-card" data-feature="flashcards">
<div class="feature-card__icon">🃏</div>
<h3>Flashcard Maker</h3>
<p>Extract key concepts into flashcards</p>
<button class="btn btn--primary">Create Flashcards</button>
</div>
</div>
<div id="generated-content" class="generated-content">
<!-- Generated content will appear here -->
</div>
</div>
</section>
<!-- Quiz Tab -->
<section id="quiz" class="tab-content">
<div class="quiz-section">
<h2>Quiz Generator</h2>
<div class="quiz-options">
<div class="form-group">
<label class="form-label">Select Study Material</label>
<select id="quiz-file-select" class="form-control">
<option value="">Select a file to create quiz from</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Number of Questions</label>
<select class="form-control" id="quiz-questions">
<option value="5">5 Questions</option>
<option value="10" selected>10 Questions</option>
<option value="15">15 Questions</option>
<option value="20">20 Questions</option>
</select>
</div>
<button class="btn btn--primary" id="generate-quiz">Generate Quiz</button>
</div>
<div id="quiz-container" class="quiz-container hidden">
<!-- Quiz questions will be generated here -->
</div>
</div>
</section>
<!-- Planner Tab -->
<section id="planner" class="tab-content">
<div class="planner-section">
<h2>Study Planner</h2>
<div class="planner-grid">
<div class="planner-card">
<h3>📅 Study Schedule</h3>
<div class="study-schedule">
<div class="schedule-item">
<span class="schedule-time">9:00 AM</span>
<span class="schedule-subject">Mathematics Review</span>
</div>
<div class="schedule-item">
<span class="schedule-time">2:00 PM</span>
<span class="schedule-subject">Science Quiz Practice</span>
</div>
<div class="schedule-item">
<span class="schedule-time">7:00 PM</span>
<span class="schedule-subject">History Flashcards</span>
</div>
</div>
<button class="btn btn--secondary">Add Study Session</button>
</div>
<div class="planner-card">
<h3>🎯 Study Goals</h3>
<div class="goals-list">
<div class="goal-item">
<input type="checkbox" id="goal1">
<label for="goal1">Complete Math Chapter 5</label>
</div>
<div class="goal-item">
<input type="checkbox" id="goal2">
<label for="goal2">Review Science Notes</label>
</div>
<div class="goal-item">
<input type="checkbox" id="goal3">
<label for="goal3">Practice 20 Quiz Questions</label>
</div>
</div>
<button class="btn btn--secondary">Add Goal</button>
</div>
<div class="planner-card">
<h3>⏰ Reminders</h3>
<div class="reminders-list">
<div class="reminder-item">
<span class="reminder-text">Math exam in 3 days</span>
<span class="status status--warning">Pending</span>
</div>
<div class="reminder-item">
<span class="reminder-text">Submit assignment</span>
<span class="status status--error">Overdue</span>
</div>
</div>
<button class="btn btn--secondary">Set Reminder</button>
</div>
</div>
</div>
</section>
</div>
</main>
<!-- Loading Modal -->
<div id="loading-modal" class="modal hidden">
<div class="modal__content">
<div class="loading-spinner"></div>
<h3 id="loading-title">Processing with AI...</h3>
<p id="loading-text">Please wait while we analyze your content and generate study materials.</p>
</div>
</div>
<script src="app.js"></script>
</body>
</html>

1297
style.css Normal file

File diff suppressed because it is too large Load Diff