Spaces:
Sleeping
Sleeping
Upload 4 files
Browse files- templates/admin.html +9 -226
- templates/base.html +9 -25
- templates/index.html +10 -196
- templates/status.html +6 -0
templates/admin.html
CHANGED
|
@@ -54,233 +54,16 @@
|
|
| 54 |
</button>
|
| 55 |
<button onclick="showTab('users')" class="admin-tab" data-tab="users">
|
| 56 |
👥 Users
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
<
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
🖥️ System Health
|
| 66 |
-
</button>
|
| 67 |
-
</nav>
|
| 68 |
-
</div>
|
| 69 |
-
</div>
|
| 70 |
-
|
| 71 |
-
<!-- Tab Content -->
|
| 72 |
-
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
| 73 |
-
<!-- Dashboard Tab -->
|
| 74 |
-
<div id="dashboardTab" class="tab-content">
|
| 75 |
-
<h2 class="text-2xl font-bold mb-6">📊 System Dashboard</h2>
|
| 76 |
-
|
| 77 |
-
<!-- System Stats Cards -->
|
| 78 |
-
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
|
| 79 |
-
<div class="stat-card">
|
| 80 |
-
<h3 class="text-lg font-semibold mb-2">👥 Total Users</h3>
|
| 81 |
-
<p id="totalUsers" class="text-3xl font-bold">-</p>
|
| 82 |
-
<p class="text-sm opacity-80">
|
| 83 |
-
<span id="newUsersWeek">-</span> this week
|
| 84 |
-
</p>
|
| 85 |
-
</div>
|
| 86 |
-
<div class="stat-card">
|
| 87 |
-
<h3 class="text-lg font-semibold mb-2">📚 Study Sessions</h3>
|
| 88 |
-
<p id="totalSessions" class="text-3xl font-bold">-</p>
|
| 89 |
-
<p class="text-sm opacity-80">All time</p>
|
| 90 |
-
</div>
|
| 91 |
-
<div class="stat-card">
|
| 92 |
-
<h3 class="text-lg font-semibold mb-2">🎯 API Tokens</h3>
|
| 93 |
-
<p id="totalTokens" class="text-3xl font-bold">-</p>
|
| 94 |
-
<p class="text-sm opacity-80">
|
| 95 |
-
$<span id="estimatedCost">-</span> estimated cost
|
| 96 |
-
</p>
|
| 97 |
-
</div>
|
| 98 |
-
<div class="stat-card">
|
| 99 |
-
<h3 class="text-lg font-semibold mb-2">📝 Content Items</h3>
|
| 100 |
-
<p id="totalContent" class="text-3xl font-bold">-</p>
|
| 101 |
-
<p class="text-sm opacity-80">Articles & flashcards</p>
|
| 102 |
-
</div>
|
| 103 |
-
</div>
|
| 104 |
-
|
| 105 |
-
<!-- Charts Section -->
|
| 106 |
-
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
|
| 107 |
-
<div class="admin-card">
|
| 108 |
-
<h3 class="text-lg font-semibold mb-4">📈 User Growth</h3>
|
| 109 |
-
<canvas id="userGrowthChart" width="400" height="200"></canvas>
|
| 110 |
-
</div>
|
| 111 |
-
<div class="admin-card">
|
| 112 |
-
<h3 class="text-lg font-semibold mb-4">💰 Token Usage Costs</h3>
|
| 113 |
-
<canvas id="tokenCostChart" width="400" height="200"></canvas>
|
| 114 |
-
</div>
|
| 115 |
-
</div>
|
| 116 |
-
|
| 117 |
-
<!-- Recent Activity -->
|
| 118 |
-
<div class="admin-card">
|
| 119 |
-
<h3 class="text-lg font-semibold mb-4">🔄 Recent Activity</h3>
|
| 120 |
-
<div id="recentActivity" class="space-y-2">
|
| 121 |
-
<div class="text-center text-gray-500 py-4">Loading recent activity...</div>
|
| 122 |
-
</div>
|
| 123 |
-
</div>
|
| 124 |
-
</div>
|
| 125 |
-
|
| 126 |
-
<!-- Users Tab -->
|
| 127 |
-
<!-- Users Tab removed: user management is not available in open version -->
|
| 128 |
-
|
| 129 |
-
<!-- Database Tab -->
|
| 130 |
-
<div id="databaseTab" class="tab-content hidden">
|
| 131 |
-
<h2 class="text-2xl font-bold mb-6">🗄️ Database Overview</h2>
|
| 132 |
-
|
| 133 |
-
<div class="admin-card">
|
| 134 |
-
<h3 class="text-lg font-semibold mb-4">📋 Database Schema</h3>
|
| 135 |
-
<div id="databaseSchema" class="space-y-4">
|
| 136 |
-
<div class="text-center text-gray-500 py-4">Loading schema...</div>
|
| 137 |
-
</div>
|
| 138 |
-
</div>
|
| 139 |
-
</div>
|
| 140 |
-
|
| 141 |
-
<!-- Token Usage Tab -->
|
| 142 |
-
<div id="tokensTab" class="tab-content hidden">
|
| 143 |
-
<div class="flex justify-between items-center mb-6">
|
| 144 |
-
<h2 class="text-2xl font-bold">🎯 Token Usage Analytics</h2>
|
| 145 |
-
<button onclick="exportTokens()" class="bg-green-500 text-white px-4 py-2 rounded-lg hover:bg-green-600">
|
| 146 |
-
📥 Export Token Data
|
| 147 |
-
</button>
|
| 148 |
-
</div>
|
| 149 |
-
|
| 150 |
-
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
| 151 |
-
<div class="admin-card">
|
| 152 |
-
<h3 class="text-lg font-semibold mb-4">📊 Usage by Provider</h3>
|
| 153 |
-
<canvas id="providerUsageChart" width="400" height="300"></canvas>
|
| 154 |
-
</div>
|
| 155 |
-
<div class="admin-card">
|
| 156 |
-
<h3 class="text-lg font-semibold mb-4">💰 Cost Breakdown</h3>
|
| 157 |
-
<canvas id="costBreakdownChart" width="400" height="300"></canvas>
|
| 158 |
-
</div>
|
| 159 |
-
</div>
|
| 160 |
-
</div>
|
| 161 |
-
|
| 162 |
-
<!-- System Health Tab -->
|
| 163 |
-
<div id="systemTab" class="tab-content hidden">
|
| 164 |
-
<h2 class="text-2xl font-bold mb-6">🖥️ System Health Monitor</h2>
|
| 165 |
-
|
| 166 |
-
<!-- System Alerts -->
|
| 167 |
-
<div class="admin-card mb-6">
|
| 168 |
-
<h3 class="text-lg font-semibold mb-4">⚠️ System Alerts</h3>
|
| 169 |
-
<div id="systemAlerts">
|
| 170 |
-
<div class="text-center text-gray-500 py-4">Loading alerts...</div>
|
| 171 |
-
</div>
|
| 172 |
-
</div>
|
| 173 |
-
|
| 174 |
-
<!-- System Metrics -->
|
| 175 |
-
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-6">
|
| 176 |
-
<div class="admin-card">
|
| 177 |
-
<h4 class="font-semibold mb-3">💾 Memory Usage</h4>
|
| 178 |
-
<div id="memoryMetrics">
|
| 179 |
-
<div class="text-sm text-gray-500">Loading...</div>
|
| 180 |
-
</div>
|
| 181 |
-
</div>
|
| 182 |
-
|
| 183 |
-
<div class="admin-card">
|
| 184 |
-
<h4 class="font-semibold mb-3">💿 Disk Usage</h4>
|
| 185 |
-
<div id="diskMetrics">
|
| 186 |
-
<div class="text-sm text-gray-500">Loading...</div>
|
| 187 |
-
</div>
|
| 188 |
-
</div>
|
| 189 |
-
|
| 190 |
-
<div class="admin-card">
|
| 191 |
-
<h4 class="font-semibold mb-3">🗄️ Database</h4>
|
| 192 |
-
<div id="databaseMetrics">
|
| 193 |
-
<div class="text-sm text-gray-500">Loading...</div>
|
| 194 |
-
</div>
|
| 195 |
-
</div>
|
| 196 |
-
</div>
|
| 197 |
-
|
| 198 |
-
<!-- Error Logs -->
|
| 199 |
-
<div class="admin-card">
|
| 200 |
-
<h3 class="text-lg font-semibold mb-4">🚨 Recent Errors</h3>
|
| 201 |
-
<div id="errorLogs">
|
| 202 |
-
<div class="text-center text-gray-500 py-4">Loading error logs...</div>
|
| 203 |
-
</div>
|
| 204 |
-
</div>
|
| 205 |
-
</div>
|
| 206 |
-
</div>
|
| 207 |
-
</div>
|
| 208 |
-
|
| 209 |
-
<!-- User Detail Modal removed -->
|
| 210 |
-
|
| 211 |
-
<!-- Toast Notifications -->
|
| 212 |
-
<div id="toastContainer" class="fixed top-4 right-4 z-50"></div>
|
| 213 |
-
|
| 214 |
-
<script>
|
| 215 |
-
// User logic, authentication, and user management removed. Admin panel is always accessible.
|
| 216 |
-
|
| 217 |
-
// Tab management (users tab removed)
|
| 218 |
-
function showTab(tabName) {
|
| 219 |
-
document.querySelectorAll('.tab-content').forEach(tab => {
|
| 220 |
-
tab.classList.add('hidden');
|
| 221 |
-
});
|
| 222 |
-
document.querySelectorAll('.admin-tab').forEach(btn => {
|
| 223 |
-
btn.classList.remove('active', 'border-blue-500', 'text-blue-600');
|
| 224 |
-
btn.classList.add('border-transparent', 'text-gray-500');
|
| 225 |
-
});
|
| 226 |
-
document.getElementById(tabName + 'Tab').classList.remove('hidden');
|
| 227 |
-
const activeBtn = document.querySelector(`[data-tab="${tabName}"]`);
|
| 228 |
-
activeBtn.classList.add('active', 'border-blue-500', 'text-blue-600');
|
| 229 |
-
activeBtn.classList.remove('border-transparent', 'text-gray-500');
|
| 230 |
-
if (tabName === 'dashboard') {
|
| 231 |
-
loadDashboard();
|
| 232 |
-
} else if (tabName === 'database') {
|
| 233 |
-
loadDatabaseSchema();
|
| 234 |
-
} else if (tabName === 'tokens') {
|
| 235 |
-
loadTokenUsage();
|
| 236 |
-
} else if (tabName === 'system') {
|
| 237 |
-
loadSystemHealth();
|
| 238 |
-
}
|
| 239 |
-
}
|
| 240 |
-
|
| 241 |
-
async function loadDashboard() {
|
| 242 |
-
try {
|
| 243 |
-
const response = await fetch('/admin/dashboard');
|
| 244 |
-
const data = await response.json();
|
| 245 |
-
|
| 246 |
-
if (data.success) {
|
| 247 |
-
const stats = data.stats;
|
| 248 |
-
|
| 249 |
-
// Update stat cards
|
| 250 |
-
document.getElementById('totalUsers').textContent = stats.users?.total || 0;
|
| 251 |
-
document.getElementById('newUsersWeek').textContent = stats.users?.new_week || 0;
|
| 252 |
-
document.getElementById('totalSessions').textContent = stats.activity?.total_sessions || 0;
|
| 253 |
-
document.getElementById('totalTokens').textContent = (stats.api_usage?.total_tokens || 0).toLocaleString();
|
| 254 |
-
document.getElementById('estimatedCost').textContent = stats.api_usage?.estimated_cost || '0.00';
|
| 255 |
-
document.getElementById('totalContent').textContent =
|
| 256 |
-
(stats.activity?.total_flashcards || 0) + (stats.activity?.total_articles || 0);
|
| 257 |
-
|
| 258 |
-
// Update recent activity
|
| 259 |
-
updateRecentActivity(stats.recent_activity || []);
|
| 260 |
-
|
| 261 |
-
// Create charts
|
| 262 |
-
createCharts(stats);
|
| 263 |
-
}
|
| 264 |
-
} catch (error) {
|
| 265 |
-
console.error('Dashboard loading error:', error);
|
| 266 |
-
showToast('Failed to load dashboard', 'error');
|
| 267 |
-
}
|
| 268 |
-
}
|
| 269 |
-
|
| 270 |
-
function updateRecentActivity(activities) {
|
| 271 |
-
const container = document.getElementById('recentActivity');
|
| 272 |
-
|
| 273 |
-
if (activities.length === 0) {
|
| 274 |
-
container.innerHTML = '<div class="text-center text-gray-500 py-4">No recent activity</div>';
|
| 275 |
-
return;
|
| 276 |
-
}
|
| 277 |
-
|
| 278 |
-
container.innerHTML = activities.map(activity => `
|
| 279 |
-
<div class="flex items-center justify-between py-2 border-b border-gray-100">
|
| 280 |
-
<div>
|
| 281 |
-
<span class="font-medium">${activity.user}</span>
|
| 282 |
-
<span class="text-gray-600">performed ${activity.activity}</span>
|
| 283 |
</div>
|
|
|
|
| 284 |
<span class="text-sm text-gray-500">${formatDate(activity.timestamp)}</span>
|
| 285 |
</div>
|
| 286 |
`).join('');
|
|
|
|
| 54 |
</button>
|
| 55 |
<button onclick="showTab('users')" class="admin-tab" data-tab="users">
|
| 56 |
👥 Users
|
| 57 |
+
{% extends "base.html" %}
|
| 58 |
+
{% block title %}Admin - English Helper{% endblock %}
|
| 59 |
+
{% block content %}
|
| 60 |
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
| 61 |
+
<h1 class="text-2xl font-bold mb-4">Painel de Administração</h1>
|
| 62 |
+
<!-- Aqui vai o conteúdo do painel admin, tabs, cards, gráficos, etc. -->
|
| 63 |
+
<p>Gerencie usuários, flashcards e estatísticas do sistema.</p>
|
| 64 |
+
<!-- Mantenha seus scripts JS e componentes aqui, se necessário -->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
</div>
|
| 66 |
+
{% endblock %}
|
| 67 |
<span class="text-sm text-gray-500">${formatDate(activity.timestamp)}</span>
|
| 68 |
</div>
|
| 69 |
`).join('');
|
templates/base.html
CHANGED
|
@@ -4,31 +4,15 @@
|
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>English Helper - Admin Panel</title>
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
height: 20px;
|
| 17 |
-
animation: spin 1s linear infinite;
|
| 18 |
-
display: inline-block;
|
| 19 |
-
margin-right: 10px;
|
| 20 |
-
}
|
| 21 |
-
@keyframes spin {
|
| 22 |
-
0% { transform: rotate(0deg); }
|
| 23 |
-
100% { transform: rotate(360deg); }
|
| 24 |
-
}
|
| 25 |
-
.table-container {
|
| 26 |
-
max-height: 600px;
|
| 27 |
-
overflow-y: auto;
|
| 28 |
-
}
|
| 29 |
-
</style>
|
| 30 |
-
</head>
|
| 31 |
-
<body class="bg-gray-100 min-h-screen">
|
| 32 |
<!-- Admin Login Modal removed: admin panel is always accessible -->
|
| 33 |
|
| 34 |
<!-- Admin Interface -->
|
|
|
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
<title>English Helper - Admin Panel</title>
|
| 7 |
+
<meta charset="UTF-8">
|
| 8 |
+
<title>{% block title %}English Helper{% endblock %}</title>
|
| 9 |
+
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
| 10 |
+
<style>
|
| 11 |
+
nav { background: #f5f5f5; padding: 1em; margin-bottom: 2em; }
|
| 12 |
+
nav a { margin-right: 1em; text-decoration: none; color: #333; font-weight: bold; }
|
| 13 |
+
nav a:hover { color: #007bff; }
|
| 14 |
+
main { max-width: 900px; margin: auto; }
|
| 15 |
+
</style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
<!-- Admin Login Modal removed: admin panel is always accessible -->
|
| 17 |
|
| 18 |
<!-- Admin Interface -->
|
templates/index.html
CHANGED
|
@@ -8,202 +8,16 @@
|
|
| 8 |
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
| 9 |
<style>
|
| 10 |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
.flashcard-container.flipped .flashcard-inner { transform: rotateY(180deg); }
|
| 22 |
-
.flashcard-front, .flashcard-back { position: absolute; width: 100%; height:100%; backface-visibility: hidden; border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; display: flex; flex-direction: column; justify-content: space-between; }
|
| 23 |
-
.flashcard-front { background-color: #f9fafb; }
|
| 24 |
-
.flashcard-back { background-color: #ffffff; transform: rotateY(180deg); }
|
| 25 |
-
.audio-btn { background: none; border: none; font-size: 1.5rem; cursor: pointer; color: #4f46e5; padding: 0; }
|
| 26 |
-
.delete-btn { position: absolute; top: 10px; right: 10px; background: none; border: none; font-size: 1.2rem; cursor: pointer; color: #9ca3af; z-index: 10; }
|
| 27 |
-
.delete-btn:hover { color: #ef4444; }
|
| 28 |
-
#activityChatDisplay { border: 1px solid #e5e7eb; border-radius: 8px; padding: 1rem; min-height: 150px; background-color: #f9fafb; }
|
| 29 |
-
.ai-prompt { border-bottom: 2px dashed #d1d5db; padding-bottom: 1rem; margin-bottom: 1rem; }
|
| 30 |
-
.user-response-display { background-color: #eef2ff; border-left: 4px solid #4f46e5; padding: 0.75rem; margin-bottom: 1rem; font-style: italic; }
|
| 31 |
-
.ai-feedback { background-color: #f0fdf4; border-left: 4px solid #22c55e; padding: 0.75rem; }
|
| 32 |
-
.ai-feedback h3 { font-weight: 700; }
|
| 33 |
-
|
| 34 |
-
/* Toast Notifications */
|
| 35 |
-
.toast-container { position: fixed; top: 20px; right: 20px; z-index: 1000; max-width: 400px; }
|
| 36 |
-
.toast { background: white; border-left: 4px solid #4f46e5; border-radius: 8px; box-shadow: 0 10px 25px rgba(0,0,0,0.15); padding: 1rem; margin-bottom: 0.5rem; opacity: 0; transform: translateX(100%); transition: all 0.3s ease; }
|
| 37 |
-
.toast.show { opacity: 1; transform: translateX(0); }
|
| 38 |
-
.toast.error { border-left-color: #ef4444; }
|
| 39 |
-
.toast.success { border-left-color: #10b981; }
|
| 40 |
-
.toast.warning { border-left-color: #f59e0b; }
|
| 41 |
-
.toast-close { float: right; background: none; border: none; font-size: 1.2rem; cursor: pointer; color: #9ca3af; }
|
| 42 |
-
|
| 43 |
-
/* Loading States & Skeleton */
|
| 44 |
-
.loading-skeleton {
|
| 45 |
-
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
|
| 46 |
-
background-size: 200% 100%;
|
| 47 |
-
animation: loading 1.5s infinite;
|
| 48 |
-
border-radius: 4px;
|
| 49 |
-
}
|
| 50 |
-
@keyframes loading { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
|
| 51 |
-
|
| 52 |
-
.skeleton-text { height: 1rem; margin: 0.25rem 0; }
|
| 53 |
-
.skeleton-text.large { height: 1.5rem; }
|
| 54 |
-
.skeleton-text.small { height: 0.75rem; }
|
| 55 |
-
.skeleton-box { height: 100px; border-radius: 8px; }
|
| 56 |
-
|
| 57 |
-
/* Enhanced Loading Spinner */
|
| 58 |
-
.spinner-container { display: flex; align-items: center; justify-content: center; padding: 2rem; }
|
| 59 |
-
.spinner-large { width: 40px; height: 40px; border-width: 4px; }
|
| 60 |
-
|
| 61 |
-
/* Fade in animations */
|
| 62 |
-
.fade-in { animation: fadeIn 0.5s ease-in; }
|
| 63 |
-
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
| 64 |
-
|
| 65 |
-
/* Slide in animations */
|
| 66 |
-
.slide-in { animation: slideIn 0.3s ease-out; }
|
| 67 |
-
@keyframes slideIn { from { transform: translateX(-20px); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
| 68 |
-
|
| 69 |
-
/* Empty states */
|
| 70 |
-
.empty-state {
|
| 71 |
-
text-align: center; padding: 3rem 1rem; color: #6b7280;
|
| 72 |
-
background: #f9fafb; border: 2px dashed #d1d5db; border-radius: 12px; margin: 1rem 0;
|
| 73 |
-
}
|
| 74 |
-
.empty-state-icon { font-size: 3rem; margin-bottom: 1rem; opacity: 0.5; }
|
| 75 |
-
.empty-state-title { font-size: 1.25rem; font-weight: 600; margin-bottom: 0.5rem; color: #374151; }
|
| 76 |
-
.empty-state-description { font-size: 0.875rem; line-height: 1.5; }
|
| 77 |
-
|
| 78 |
-
/* Enhanced Flashcards - Responsive */
|
| 79 |
-
.flashcard-container { perspective: 1000px; position: relative; }
|
| 80 |
-
.flashcard-inner { position: relative; width: 100%; min-height: 280px; transition: transform 0.6s; transform-style: preserve-3d; }
|
| 81 |
-
.flashcard-container.flipped .flashcard-inner { transform: rotateY(180deg); }
|
| 82 |
-
.flashcard-front, .flashcard-back {
|
| 83 |
-
position: absolute; width: 100%; height: 100%; min-height: 280px; backface-visibility: hidden;
|
| 84 |
-
border: 1px solid #e5e7eb; border-radius: 12px; padding: 20px; display: flex; flex-direction: column;
|
| 85 |
-
justify-content: space-between; word-wrap: break-word; overflow-wrap: break-word;
|
| 86 |
-
}
|
| 87 |
-
.flashcard-front { background-color: #f9fafb; }
|
| 88 |
-
.flashcard-back { background-color: #ffffff; transform: rotateY(180deg); }
|
| 89 |
-
|
| 90 |
-
/* Responsive text handling */
|
| 91 |
-
.flashcard-text {
|
| 92 |
-
font-size: 1rem; line-height: 1.5; max-height: 150px; overflow-y: auto;
|
| 93 |
-
word-break: break-word; hyphens: auto; padding: 0.25rem 0;
|
| 94 |
-
}
|
| 95 |
-
|
| 96 |
-
/* Mobile responsiveness */
|
| 97 |
-
@media (max-width: 768px) {
|
| 98 |
-
.flashcard-text { font-size: 0.9rem; max-height: 120px; line-height: 1.4; }
|
| 99 |
-
.flashcard-container { margin-bottom: 1rem; }
|
| 100 |
-
.flashcard-inner { min-height: 250px; }
|
| 101 |
-
.flashcard-front, .flashcard-back { min-height: 250px; padding: 16px; }
|
| 102 |
-
.card { padding: 16px; }
|
| 103 |
-
body { padding: 0.5rem; }
|
| 104 |
-
.container { padding: 0; }
|
| 105 |
-
|
| 106 |
-
/* Adjust grid for mobile */
|
| 107 |
-
.grid.grid-cols-1.md\\:grid-cols-3 { grid-template-columns: 1fr; }
|
| 108 |
-
.grid.grid-cols-1.lg\\:grid-cols-2 { grid-template-columns: 1fr; }
|
| 109 |
-
}
|
| 110 |
-
|
| 111 |
-
@media (max-width: 480px) {
|
| 112 |
-
.flashcard-text { font-size: 0.85rem; max-height: 100px; }
|
| 113 |
-
.flashcard-inner { min-height: 220px; }
|
| 114 |
-
.flashcard-front, .flashcard-back { min-height: 220px; padding: 12px; }
|
| 115 |
-
|
| 116 |
-
/* Make buttons more touch-friendly */
|
| 117 |
-
.btn { padding: 0.75rem 1rem; min-height: 44px; }
|
| 118 |
-
.audio-btn { font-size: 1.25rem; padding: 0.5rem; }
|
| 119 |
-
.delete-btn { font-size: 1rem; padding: 0.5rem; }
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
/* Text overflow handling */
|
| 123 |
-
.text-truncate {
|
| 124 |
-
overflow: hidden;
|
| 125 |
-
text-overflow: ellipsis;
|
| 126 |
-
white-space: nowrap;
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
.text-wrap {
|
| 130 |
-
white-space: normal;
|
| 131 |
-
word-wrap: break-word;
|
| 132 |
-
overflow-wrap: break-word;
|
| 133 |
-
}
|
| 134 |
-
|
| 135 |
-
/* Form Validation */
|
| 136 |
-
.form-error { border-color: #ef4444 !important; }
|
| 137 |
-
.error-message { color: #ef4444; font-size: 0.875rem; margin-top: 0.25rem; }
|
| 138 |
-
|
| 139 |
-
/* Enhanced buttons */
|
| 140 |
-
.btn { position: relative; overflow: hidden; }
|
| 141 |
-
.btn::after { content: ''; position: absolute; top: 50%; left: 50%; width: 0; height: 0; border-radius: 50%; background: rgba(255,255,255,0.3); transform: translate(-50%, -50%); transition: width 0.3s, height 0.3s; }
|
| 142 |
-
.btn:active::after { width: 300px; height: 300px; }
|
| 143 |
-
|
| 144 |
-
/* Pulse animation for mic button */
|
| 145 |
-
.mic-button.is-recording { animation: pulse 1.5s infinite; }
|
| 146 |
-
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.1); } }
|
| 147 |
-
|
| 148 |
-
/* Auth Modal */
|
| 149 |
-
.modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); }
|
| 150 |
-
.modal.show { display: block; }
|
| 151 |
-
.modal-content {
|
| 152 |
-
background-color: white; margin: 5% auto; padding: 0; width: 90%; max-width: 400px;
|
| 153 |
-
border-radius: 12px; position: relative; animation: modalSlideIn 0.3s ease;
|
| 154 |
-
}
|
| 155 |
-
@keyframes modalSlideIn { from { opacity: 0; transform: translateY(-50px); } to { opacity: 1; transform: translateY(0); } }
|
| 156 |
-
.modal-header { padding: 1.5rem; border-bottom: 1px solid #e5e7eb; }
|
| 157 |
-
.modal-body { padding: 1.5rem; }
|
| 158 |
-
.modal-footer { padding: 1rem 1.5rem; border-top: 1px solid #e5e7eb; display: flex; justify-content: flex-end; gap: 0.5rem; }
|
| 159 |
-
.close { color: #aaa; float: right; font-size: 28px; font-weight: bold; cursor: pointer; }
|
| 160 |
-
.close:hover { color: #000; }
|
| 161 |
-
|
| 162 |
-
/* Auth Form */
|
| 163 |
-
.auth-form { display: none; }
|
| 164 |
-
.auth-form.active { display: block; }
|
| 165 |
-
.form-group { margin-bottom: 1rem; }
|
| 166 |
-
.form-group label { display: block; margin-bottom: 0.25rem; font-weight: 500; color: #374151; }
|
| 167 |
-
.form-group input { width: 100%; padding: 0.75rem; border: 1px solid #d1d5db; border-radius: 6px; font-size: 0.875rem; }
|
| 168 |
-
.form-group input:focus { outline: none; border-color: #4f46e5; box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); }
|
| 169 |
-
|
| 170 |
-
/* User Menu */
|
| 171 |
-
.user-menu { position: relative; display: inline-block; }
|
| 172 |
-
.user-menu-content { display: none; position: absolute; right: 0; background-color: white; min-width: 200px; box-shadow: 0 8px 16px rgba(0,0,0,0.1); border-radius: 8px; z-index: 1; border: 1px solid #e5e7eb; }
|
| 173 |
-
.user-menu.show .user-menu-content { display: block; }
|
| 174 |
-
.user-menu-item { padding: 0.75rem 1rem; display: block; color: #374151; text-decoration: none; border-bottom: 1px solid #f3f4f6; }
|
| 175 |
-
.user-menu-item:hover { background-color: #f9fafb; }
|
| 176 |
-
.user-menu-item:last-child { border-bottom: none; }
|
| 177 |
-
|
| 178 |
-
/* Header Auth Section */
|
| 179 |
-
.auth-section { display: flex; align-items: center; gap: 0.5rem; }
|
| 180 |
-
.user-avatar { width: 40px; height: 40px; border-radius: 50%; background-color: #4f46e5; color: white; display: flex; align-items: center; justify-content: center; font-weight: 600; cursor: pointer; }
|
| 181 |
-
|
| 182 |
-
/* Content Curation Styles */
|
| 183 |
-
.content-item {
|
| 184 |
-
border: 1px solid #e5e7eb; border-radius: 8px; padding: 1rem; margin-bottom: 1rem;
|
| 185 |
-
background: white; transition: all 0.2s;
|
| 186 |
-
}
|
| 187 |
-
.content-item:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.1); border-color: #4f46e5; }
|
| 188 |
-
.content-item-title { font-weight: 600; color: #1f2937; margin-bottom: 0.5rem; }
|
| 189 |
-
.content-item-meta { font-size: 0.75rem; color: #6b7280; margin-bottom: 0.5rem; }
|
| 190 |
-
.content-item-summary { font-size: 0.875rem; color: #4b5563; line-height: 1.5; }
|
| 191 |
-
.difficulty-badge {
|
| 192 |
-
display: inline-block; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 500;
|
| 193 |
-
}
|
| 194 |
-
.difficulty-easy { background-color: #dcfce7; color: #166534; }
|
| 195 |
-
.difficulty-medium { background-color: #fef3c7; color: #92400e; }
|
| 196 |
-
.difficulty-hard { background-color: #fee2e2; color: #991b1b; }
|
| 197 |
-
|
| 198 |
-
/* Analytics Styles */
|
| 199 |
-
.metric-card {
|
| 200 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
| 201 |
-
color: white; border-radius: 12px; padding: 1.5rem; text-align: center;
|
| 202 |
-
}
|
| 203 |
-
.metric-value { font-size: 2rem; font-weight: bold; margin-bottom: 0.5rem; }
|
| 204 |
-
.metric-label { font-size: 0.875rem; opacity: 0.9; }
|
| 205 |
-
|
| 206 |
-
/* Study Plan Styles */
|
| 207 |
.plan-activity {
|
| 208 |
display: flex; align-items: center; padding: 0.75rem; border-radius: 6px;
|
| 209 |
border-left: 4px solid #4f46e5; background-color: #f8fafc; margin-bottom: 0.5rem;
|
|
|
|
| 8 |
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
| 9 |
<style>
|
| 10 |
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
| 11 |
+
{% extends "base.html" %}
|
| 12 |
+
{% block title %}Início - English Helper{% endblock %}
|
| 13 |
+
{% block content %}
|
| 14 |
+
<div class="container mx-auto max-w-5xl">
|
| 15 |
+
<!-- Conteúdo principal da página inicial aqui -->
|
| 16 |
+
<h1 class="text-2xl font-bold mb-4">Bem-vindo ao English Helper!</h1>
|
| 17 |
+
<p>Use o menu acima para navegar entre as funções do sistema.</p>
|
| 18 |
+
<!-- Você pode inserir aqui os blocos e componentes do seu app -->
|
| 19 |
+
</div>
|
| 20 |
+
{% endblock %}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
.plan-activity {
|
| 22 |
display: flex; align-items: center; padding: 0.75rem; border-radius: 6px;
|
| 23 |
border-left: 4px solid #4f46e5; background-color: #f8fafc; margin-bottom: 0.5rem;
|
templates/status.html
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{% extends "base.html" %}
|
| 2 |
+
{% block title %}Status - English Helper{% endblock %}
|
| 3 |
+
{% block content %}
|
| 4 |
+
<h1>Status do Sistema</h1>
|
| 5 |
+
<p>Veja informações sobre o funcionamento e APIs do sistema.</p>
|
| 6 |
+
{% endblock %}
|