SmartMate / templates /index.html
ngwakomadikwe's picture
Update templates/index.html
8e2b003 verified
raw
history blame
28.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ThutoAI - Your Student Assistant</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
}
.app-container {
display: flex;
width: 100%;
min-height: 100vh;
}
/* Sidebar */
.sidebar {
width: 280px;
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
box-shadow: 2px 0 20px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
}
.sidebar-header {
padding: 30px 20px;
text-align: center;
border-bottom: 1px solid rgba(0,0,0,0.1);
}
.sidebar-header h1 {
color: #333;
font-size: 28px;
margin-bottom: 5px;
}
.sidebar-header p {
color: #666;
font-size: 14px;
}
.sidebar-nav {
flex: 1;
padding: 20px 0;
}
.nav-item {
padding: 15px 25px;
margin: 5px 15px;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s ease;
color: #555;
font-weight: 500;
}
.nav-item:hover {
background: rgba(102, 126, 234, 0.1);
color: #667eea;
}
.nav-item.active {
background: #667eea;
color: white;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.nav-item span {
font-size: 16px;
}
.sidebar-footer {
padding: 20px;
border-top: 1px solid rgba(0,0,0,0.1);
text-align: center;
}
.admin-link {
color: #667eea;
text-decoration: none;
font-size: 14px;
padding: 8px 16px;
border: 1px solid #667eea;
border-radius: 20px;
transition: all 0.3s ease;
}
.admin-link:hover {
background: #667eea;
color: white;
}
/* Main Content */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
background: rgba(255, 255, 255, 0.9);
backdrop-filter: blur(10px);
}
.content-header {
padding: 30px 40px;
border-bottom: 1px solid rgba(0,0,0,0.1);
background: rgba(255, 255, 255, 0.8);
}
.content-header h2 {
color: #333;
font-size: 32px;
margin-bottom: 8px;
}
.content-header p {
color: #666;
font-size: 16px;
}
.content-body {
flex: 1;
padding: 40px;
overflow-y: auto;
}
/* Views */
.view {
display: block;
}
.view.hidden {
display: none;
}
/* Dashboard Cards */
.dashboard-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 25px;
margin-bottom: 30px;
}
.dashboard-card {
background: white;
border-radius: 20px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.dashboard-card:hover {
transform: translateY(-5px);
box-shadow: 0 20px 40px rgba(0,0,0,0.15);
}
.card-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.card-icon {
font-size: 24px;
margin-right: 12px;
}
.card-title {
font-size: 18px;
font-weight: 600;
color: #333;
}
.card-content {
color: #666;
}
.card-item {
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
}
.card-item:last-child {
border-bottom: none;
}
.item-title {
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.item-details {
font-size: 14px;
color: #666;
}
/* Chat Interface */
.chat-container {
display: flex;
flex-direction: column;
height: 600px;
background: white;
border-radius: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
overflow: hidden;
}
.chat-messages {
flex: 1;
padding: 25px;
overflow-y: auto;
background: #f8f9fa;
}
.welcome-message {
text-align: center;
padding: 40px 20px;
background: white;
border-radius: 15px;
margin-bottom: 20px;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.welcome-message h3 {
color: #333;
margin-bottom: 15px;
font-size: 24px;
}
.welcome-message p {
color: #666;
margin-bottom: 10px;
}
.message {
margin-bottom: 20px;
display: flex;
align-items: flex-start;
}
.message.user {
justify-content: flex-end;
}
.message-content {
max-width: 70%;
padding: 15px 20px;
border-radius: 20px;
word-wrap: break-word;
}
.message.user .message-content {
background: #667eea;
color: white;
border-bottom-right-radius: 5px;
}
.message.assistant .message-content {
background: white;
color: #333;
border-bottom-left-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.chat-input-container {
padding: 25px;
background: white;
border-top: 1px solid #e0e0e0;
}
.chat-input-form {
display: flex;
gap: 15px;
}
.chat-input {
flex: 1;
padding: 15px 20px;
border: 2px solid #e0e0e0;
border-radius: 25px;
font-size: 16px;
outline: none;
transition: border-color 0.3s ease;
}
.chat-input:focus {
border-color: #667eea;
}
.send-button {
padding: 15px 25px;
background: #667eea;
color: white;
border: none;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
font-weight: 600;
transition: background 0.3s ease;
}
.send-button:hover {
background: #5a6fd8;
}
.send-button:disabled {
background: #ccc;
cursor: not-allowed;
}
/* Loading Animation */
.loading {
display: inline-block;
width: 20px;
height: 20px;
border: 3px solid #f3f3f3;
border-top: 3px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Responsive Design */
@media (max-width: 768px) {
.app-container {
flex-direction: column;
}
.sidebar {
width: 100%;
height: auto;
}
.sidebar-nav {
display: flex;
overflow-x: auto;
padding: 10px 0;
}
.nav-item {
white-space: nowrap;
min-width: 120px;
margin: 0 5px;
}
.content-body {
padding: 20px;
}
.dashboard-grid {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="app-container">
<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-header">
<h1>πŸŽ“ ThutoAI</h1>
<p>Your Student Assistant</p>
</div>
<div class="sidebar-nav">
<div class="nav-item active" data-view="dashboard">
<span>πŸ“Š Dashboard</span>
</div>
<div class="nav-item" data-view="chat">
<span>πŸ’¬ AI Chat</span>
</div>
<div class="nav-item" data-view="announcements">
<span>πŸ“’ Announcements</span>
</div>
<div class="nav-item" data-view="exams">
<span>πŸ“ Exams</span>
</div>
<div class="nav-item" data-view="grades">
<span>πŸ“Š Grades</span>
</div>
</div>
<div class="sidebar-footer">
<a href="/admin/login" class="admin-link">πŸ” Admin Panel</a>
</div>
</div>
<!-- Main Content -->
<div class="main-content">
<!-- Dashboard View -->
<div id="dashboard-view" class="view">
<div class="content-header">
<h2>Student Dashboard</h2>
<p>Your academic overview and important updates</p>
</div>
<div class="content-body">
<div class="dashboard-grid" id="dashboardGrid">
<div class="loading">Loading your dashboard...</div>
</div>
</div>
</div>
<!-- Chat View -->
<div id="chat-view" class="view hidden">
<div class="content-header">
<h2>AI Chat Assistant</h2>
<p>Ask me anything about your studies, school, or get academic help</p>
</div>
<div class="content-body">
<div class="chat-container">
<div class="chat-messages" id="chatMessages">
<div class="welcome-message">
<h3>πŸ‘‹ Hello! I'm your ThutoAI Assistant</h3>
<p>I can help you with:</p>
<ul style="text-align: left; display: inline-block; margin-top: 10px;">
<li>πŸ“š Study questions and academic help</li>
<li>πŸ“… Information about exams and assignments</li>
<li>πŸ“Š Your grades and academic performance</li>
<li>πŸ“’ School announcements and events</li>
<li>πŸ’‘ Study tips and motivation</li>
</ul>
<p style="margin-top: 15px;">What would you like to know?</p>
</div>
</div>
<div class="chat-input-container">
<form class="chat-input-form" id="chatForm">
<input type="text" class="chat-input" id="chatInput" placeholder="Ask me anything about your studies..." required>
<button type="submit" class="send-button" id="sendButton">Send</button>
</form>
</div>
</div>
</div>
</div>
<!-- Announcements View -->
<div id="announcements-view" class="view hidden">
<div class="content-header">
<h2>School Announcements</h2>
<p>Latest news and updates from your school</p>
</div>
<div class="content-body">
<div class="dashboard-grid" id="announcementsGrid">
<div class="loading">Loading announcements...</div>
</div>
</div>
</div>
<!-- Exams View -->
<div id="exams-view" class="view hidden">
<div class="content-header">
<h2>Upcoming Exams</h2>
<p>Your examination schedule and important details</p>
</div>
<div class="content-body">
<div class="dashboard-grid" id="examsGrid">
<div class="loading">Loading exams...</div>
</div>
</div>
</div>
<!-- Grades View -->
<div id="grades-view" class="view hidden">
<div class="content-header">
<h2>Your Grades</h2>
<p>Academic performance and assessment results</p>
</div>
<div class="content-body">
<div class="dashboard-grid" id="gradesGrid">
<div class="loading">Loading grades...</div>
</div>
</div>
</div>
</div>
</div>
<script>
// Global variables
let currentView = 'dashboard';
// DOM elements
const navItems = document.querySelectorAll('.nav-item');
const views = document.querySelectorAll('.view');
const chatForm = document.getElementById('chatForm');
const chatInput = document.getElementById('chatInput');
const chatMessages = document.getElementById('chatMessages');
const sendButton = document.getElementById('sendButton');
// Initialize app
document.addEventListener('DOMContentLoaded', () => {
setupNavigation();
loadDashboard();
});
// Navigation setup
function setupNavigation() {
navItems.forEach(item => {
item.addEventListener('click', () => {
const viewName = item.dataset.view;
switchView(viewName);
});
});
}
// Switch between views
function switchView(viewName) {
// Update navigation
navItems.forEach(item => {
item.classList.toggle('active', item.dataset.view === viewName);
});
// Update views
views.forEach(view => {
view.classList.toggle('hidden', !view.id.startsWith(viewName));
});
currentView = viewName;
// Load view-specific data
switch(viewName) {
case 'dashboard':
loadDashboard();
break;
case 'announcements':
loadAnnouncements();
break;
case 'exams':
loadExams();
break;
case 'grades':
loadGrades();
break;
}
}
// Load dashboard data
async function loadDashboard() {
try {
const response = await fetch('/dashboard');
const data = await response.json();
if (data.success) {
renderDashboard(data.data);
} else {
console.error('Dashboard error:', data.error);
}
} catch (error) {
console.error('Dashboard fetch error:', error);
}
}
// Render dashboard
function renderDashboard(data) {
const grid = document.getElementById('dashboardGrid');
grid.innerHTML = '';
// Recent announcements card
if (data.announcements && data.announcements.length > 0) {
grid.appendChild(createDashboardCard('πŸ“’', 'Recent Announcements', data.announcements, 'announcements'));
}
// Upcoming exams card
if (data.upcoming_exams && data.upcoming_exams.length > 0) {
grid.appendChild(createDashboardCard('πŸ“', 'Upcoming Exams', data.upcoming_exams, 'exams'));
}
// Upcoming tests card
if (data.upcoming_tests && data.upcoming_tests.length > 0) {
grid.appendChild(createDashboardCard('πŸ“š', 'Upcoming Tests', data.upcoming_tests, 'tests'));
}
// Recent grades card
if (data.recent_grades && data.recent_grades.length > 0) {
grid.appendChild(createDashboardCard('πŸ“Š', 'Recent Grades', data.recent_grades, 'grades'));
}
// Upcoming events card
if (data.upcoming_events && data.upcoming_events.length > 0) {
grid.appendChild(createDashboardCard('πŸŽ‰', 'Upcoming Events', data.upcoming_events, 'events'));
}
if (grid.children.length === 0) {
grid.innerHTML = '<div class="welcome-message"><h3>Welcome to ThutoAI!</h3><p>Your dashboard will show information as it becomes available.</p></div>';
}
}
function createDashboardCard(icon, title, items, type) {
const card = document.createElement('div');
card.className = 'dashboard-card';
const header = document.createElement('div');
header.className = 'card-header';
header.innerHTML = `
<span class="card-icon">${icon}</span>
<span class="card-title">${title}</span>
`;
const content = document.createElement('div');
content.className = 'card-content';
items.slice(0, 3).forEach(item => {
const itemDiv = document.createElement('div');
itemDiv.className = 'card-item';
let itemContent = '';
switch(type) {
case 'announcements':
itemContent = `
<div class="item-title">${item.title}</div>
<div class="item-details">${item.content.substring(0, 100)}...</div>
`;
break;
case 'exams':
itemContent = `
<div class="item-title">${item.name}</div>
<div class="item-details">${item.subject} - ${item.date} at ${item.time}</div>
`;
break;
case 'tests':
itemContent = `
<div class="item-title">${item.title}</div>
<div class="item-details">${item.subject} - Due: ${item.due_date}</div>
`;
break;
case 'grades':
itemContent = `
<div class="item-title">${item.subject}</div>
<div class="item-details">${item.percentage}% (${item.grade}) - ${item.name}</div>
`;
break;
case 'events':
itemContent = `
<div class="item-title">${item.name}</div>
<div class="item-details">${item.date} - ${item.location}</div>
`;
break;
}
itemDiv.innerHTML = itemContent;
content.appendChild(itemDiv);
});
card.appendChild(header);
card.appendChild(content);
return card;
}
// Load announcements
async function loadAnnouncements() {
try {
const response = await fetch('/announcements');
const data = await response.json();
if (data.success) {
renderAnnouncements(data.announcements);
}
} catch (error) {
console.error('Announcements fetch error:', error);
}
}
function renderAnnouncements(announcements) {
const grid = document.getElementById('announcementsGrid');
grid.innerHTML = '';
if (announcements.length === 0) {
grid.innerHTML = '<div class="welcome-message"><h3>No Announcements</h3><p>No announcements available at the moment.</p></div>';
return;
}
announcements.forEach(ann => {
const card = document.createElement('div');
card.className = 'dashboard-card';
card.innerHTML = `
<div class="card-header">
<span class="card-icon">πŸ“’</span>
<span class="card-title">${ann.title}</span>
</div>
<div class="card-content">
<p>${ann.content}</p>
<div class="item-details" style="margin-top: 10px;">
Priority: ${ann.priority} | Date: ${new Date(ann.date).toLocaleDateString()}
</div>
</div>
`;
grid.appendChild(card);
});
}
// Load exams
async function loadExams() {
try {
const response = await fetch('/exams');
const data = await response.json();
if (data.success) {
renderExams(data.exams);
}
} catch (error) {
console.error('Exams fetch error:', error);
}
}
function renderExams(exams) {
const grid = document.getElementById('examsGrid');
grid.innerHTML = '';
if (exams.length === 0) {
grid.innerHTML = '<div class="welcome-message"><h3>No Upcoming Exams</h3><p>No exams scheduled at the moment.</p></div>';
return;
}
exams.forEach(exam => {
const card = document.createElement('div');
card.className = 'dashboard-card';
card.innerHTML = `
<div class="card-header">
<span class="card-icon">πŸ“</span>
<span class="card-title">${exam.name}</span>
</div>
<div class="card-content">
<div class="card-item">
<div class="item-title">Subject: ${exam.subject}</div>
</div>
<div class="card-item">
<div class="item-title">Date & Time: ${exam.date} at ${exam.time}</div>
</div>
<div class="card-item">
<div class="item-title">Location: ${exam.location}</div>
</div>
${exam.instructions ? `<div class="card-item"><div class="item-details">${exam.instructions}</div></div>` : ''}
</div>
`;
grid.appendChild(card);
});
}
// Load grades
async function loadGrades() {
try {
const response = await fetch('/grades');
const data = await response.json();
if (data.success) {
renderGrades(data.grades);
}
} catch (error) {
console.error('Grades fetch error:', error);
}
}
function renderGrades(grades) {
const grid = document.getElementById('gradesGrid');
grid.innerHTML = '';
if (grades.length === 0) {
grid.innerHTML = '<div class="welcome-message"><h3>No Grades Available</h3><p>No grades recorded yet.</p></div>';
return;
}
grades.forEach(grade => {
const card = document.createElement('div');
card.className = 'dashboard-card';
card.innerHTML = `
<div class="card-header">
<span class="card-icon">πŸ“Š</span>
<span class="card-title">${grade.subject}</span>
</div>
<div class="card-content">
<div class="card-item">
<div class="item-title">${grade.name}</div>
<div class="item-details">${grade.type}</div>
</div>
<div class="card-item">
<div class="item-title">Score: ${grade.marks_obtained}/${grade.total_marks} (${grade.percentage}%)</div>
<div class="item-details">Grade: ${grade.grade}</div>
</div>
<div class="card-item">
<div class="item-details">Date: ${grade.date}</div>
</div>
${grade.comments ? `<div class="card-item"><div class="item-details">Comments: ${grade.comments}</div></div>` : ''}
</div>
`;
grid.appendChild(card);
});
}
// Chat functionality
if (chatForm) {
chatForm.addEventListener('submit', async (e) => {
e.preventDefault();
const message = chatInput.value.trim();
if (!message) return;
// Add user message to chat
addMessage(message, 'user');
chatInput.value = '';
sendButton.disabled = true;
sendButton.innerHTML = '<span class="loading"></span>';
try {
const response = await fetch('/chat', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message: message })
});
const data = await response.json();
if (data.reply) {
addMessage(data.reply, 'assistant');
} else {
addMessage('Sorry, I encountered an error. Please try again.', 'assistant');
}
} catch (error) {
addMessage('Sorry, I could not connect to the server. Please check your connection and try again.', 'assistant');
} finally {
sendButton.disabled = false;
sendButton.innerHTML = 'Send';
}
});
}
function addMessage(content, sender) {
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentDiv.innerHTML = content.replace(/\n/g, '<br>');
messageDiv.appendChild(contentDiv);
chatMessages.appendChild(messageDiv);
// Scroll to bottom
chatMessages.scrollTop = chatMessages.scrollHeight;
}
</script>
</body>
</html>