amauricunha commited on
Commit
25cde20
·
verified ·
1 Parent(s): e0cf074

Delete templates/admin.html

Browse files
Files changed (1) hide show
  1. templates/admin.html +0 -297
templates/admin.html DELETED
@@ -1,297 +0,0 @@
1
- {% extends "base.html" %}
2
- {% block title %}Admin - English Helper{% endblock %}
3
- {% block content %}
4
- <style>
5
- .spinner {
6
- border: 3px solid #f3f3f3;
7
- border-top: 3px solid #3498db;
8
- border-radius: 50%;
9
- width: 20px;
10
- height: 20px;
11
- animation: spin 1s linear infinite;
12
- display: inline-block;
13
- margin-right: 10px;
14
- }
15
- @keyframes spin {
16
- 0% { transform: rotate(0deg); }
17
- 100% { transform: rotate(360deg); }
18
- }
19
- .table-container {
20
- max-height: 600px;
21
- overflow-y: auto;
22
- }
23
- </style>
24
-
25
- <div id="adminInterface">
26
- <!-- Header -->
27
- <header class="bg-white shadow-md">
28
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
29
- <div class="flex justify-between items-center py-4">
30
- <div class="flex items-center">
31
- <h1 class="text-2xl font-bold text-gray-900">🎓 English Helper Admin</h1>
32
- </div>
33
- </div>
34
- </div>
35
- </header>
36
-
37
- <!-- Floating Save Data Button -->
38
- <div class="fixed top-4 right-4 z-50">
39
- <button id="saveDataBtn" class="px-4 py-2 bg-blue-600 text-white rounded shadow">Salvar dados</button>
40
- </div>
41
-
42
- <!-- Navigation Tabs -->
43
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 mt-6">
44
- <div class="border-b border-gray-200">
45
- <nav class="-mb-px flex space-x-8">
46
- <button onclick="showTab('dashboard')" class="admin-tab active" data-tab="dashboard">
47
- 📊 Dashboard
48
- </button>
49
- <button onclick="showTab('users')" class="admin-tab" data-tab="users">
50
- 👥 Users
51
- </button>
52
- <button onclick="showTab('database')" class="admin-tab" data-tab="database">
53
- 🗄️ Database
54
- </button>
55
- <button onclick="showTab('tokens')" class="admin-tab" data-tab="tokens">
56
- 💳 Token Usage
57
- </button>
58
- <button onclick="showTab('system')" class="admin-tab" data-tab="system">
59
- 🩺 System Health
60
- </button>
61
- </nav>
62
- </div>
63
- </div>
64
-
65
- <!-- Tab Content -->
66
- <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
67
- <!-- Dashboard Tab -->
68
- <div id="dashboardTab" class="tab-content">
69
- <!-- ...existing dashboard content... -->
70
- </div>
71
-
72
- <!-- Users Tab -->
73
- <div id="usersTab" class="tab-content hidden">
74
- <!-- ...existing users content... -->
75
- </div>
76
-
77
- <!-- Database Tab -->
78
- <div id="databaseTab" class="tab-content hidden">
79
- <!-- ...existing database content... -->
80
- </div>
81
-
82
- <!-- Token Usage Tab -->
83
- <div id="tokensTab" class="tab-content hidden">
84
- <!-- ...existing token usage content... -->
85
- </div>
86
-
87
- <!-- System Health Tab -->
88
- <div id="systemTab" class="tab-content hidden">
89
- <!-- ...existing system health content... -->
90
- </div>
91
- </div>
92
- </div>
93
-
94
- <!-- User Detail Modal -->
95
- <div id="userDetailModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
96
- <div class="bg-white rounded-lg p-6 max-w-4xl w-full mx-4 max-h-screen overflow-y-auto">
97
- <div class="flex justify-between items-center mb-4">
98
- <!-- ...existing modal header... -->
99
- </div>
100
- <div id="userDetailContent">
101
- <!-- ...existing modal content... -->
102
- </div>
103
- </div>
104
- </div>
105
-
106
- <!-- Toast Notifications -->
107
- <div id="toastContainer" class="fixed top-4 right-4 z-50"></div>
108
-
109
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
110
- <script>
111
- // Minimal, consolidated admin UI JS that talks to existing backend endpoints
112
- let currentPage = 1;
113
- let usersData = [];
114
-
115
- document.addEventListener('DOMContentLoaded', () => {
116
- showTab('dashboard');
117
- loadDashboard();
118
- loadUsers();
119
- loadDatabaseSchema();
120
- loadTokenUsage();
121
- loadSystemHealth();
122
- });
123
-
124
- function showTab(tabName) {
125
- document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
126
- const active = document.getElementById(tabName + 'Tab');
127
- if (active) active.classList.remove('hidden');
128
-
129
- document.querySelectorAll('.admin-tab').forEach(btn => btn.classList.remove('active'));
130
- const btn = document.querySelector(`.admin-tab[data-tab="${tabName}"]`);
131
- if (btn) btn.classList.add('active');
132
- }
133
-
134
- async function loadDashboard() {
135
- try {
136
- const res = await fetch('/admin/stats');
137
- const data = await res.json();
138
-
139
- const container = document.getElementById('dashboardTab');
140
- if (!container) return;
141
-
142
- container.innerHTML = `
143
- <h2 class="text-xl font-semibold mb-4">System Dashboard</h2>
144
- <div class="grid grid-cols-1 md:grid-cols-4 gap-6">
145
- <div class="p-4 bg-white rounded shadow"> <div class="text-sm text-gray-500">Total Users</div> <div class="text-2xl font-bold">${data.total_users ?? '-'}</div></div>
146
- <div class="p-4 bg-white rounded shadow"> <div class="text-sm text-gray-500">Study Sessions</div> <div class="text-2xl font-bold">-</div></div>
147
- <div class="p-4 bg-white rounded shadow"> <div class="text-sm text-gray-500">API Tokens</div> <div class="text-2xl font-bold">-</div></div>
148
- <div class="p-4 bg-white rounded shadow"> <div class="text-sm text-gray-500">Content Items</div> <div class="text-2xl font-bold">-</div></div>
149
- </div>
150
- <div class="mt-6">
151
- <canvas id="userGrowthChart" height="120"></canvas>
152
- </div>
153
- `;
154
-
155
- // simple user growth sample chart
156
- const ctx = document.getElementById('userGrowthChart')?.getContext('2d');
157
- if (ctx) {
158
- new Chart(ctx, { type: 'line', data: { labels: ['-','-','-'], datasets:[{label:'Users', data:[0,0, data.total_users || 0], borderColor:'rgb(59,130,246)', fill:false}]}, options:{responsive:true}});
159
- }
160
- } catch (e) {
161
- console.error('Dashboard load error', e);
162
- }
163
- }
164
-
165
- async function loadUsers() {
166
- try {
167
- const res = await fetch('/users');
168
- const data = await res.json();
169
- const users = data.users || [];
170
- usersData = users;
171
-
172
- const container = document.getElementById('usersTab');
173
- if (!container) return;
174
-
175
- if (users.length === 0) {
176
- container.innerHTML = '<div class="text-center py-8">No users found</div>';
177
- return;
178
- }
179
-
180
- container.innerHTML = `
181
- <div class="overflow-x-auto bg-white rounded shadow">
182
- <table class="w-full text-left">
183
- <thead class="bg-gray-50"><tr><th class="p-3">User ID</th><th class="p-3">Stats</th><th class="p-3">Actions</th></tr></thead>
184
- <tbody id="usersTableBody">${users.map(u=>`<tr class="border-b"><td class="p-3">${u}</td><td class="p-3">-</td><td class="p-3"><button onclick="viewUser('${u}')" class="text-blue-600">View</button></td></tr>`).join('')}</tbody>
185
- </table>
186
- </div>
187
- `;
188
- } catch (e) {
189
- console.error('loadUsers error', e);
190
- showToast('Failed to load users', 'error');
191
- }
192
- }
193
-
194
- async function viewUser(userId) {
195
- try {
196
- const res = await fetch(`/user/${encodeURIComponent(userId)}/analytics`);
197
- const data = await res.json();
198
- const modal = document.getElementById('userDetailModal');
199
- const content = document.getElementById('userDetailContent');
200
- content.innerHTML = `
201
- <h3 class="text-lg font-semibold mb-2">User: ${userId}</h3>
202
- <div class="text-sm">Total flashcards: ${data.total_flashcards ?? 0}</div>
203
- <div class="text-sm">Total conversations: ${data.total_conversations ?? 0}</div>
204
- <div class="text-sm">Total sessions: ${data.total_sessions ?? 0}</div>
205
- <div class="mt-4">
206
- <h4 class="font-medium">Recent activity</h4>
207
- <div class="max-h-48 overflow-y-auto text-sm">${(data.recent_activity||[]).map(a=>`<div class="py-1 border-b">${a.timestamp || ''} - ${a.get || a.action || JSON.stringify(a)}</div>`).join('')}</div>
208
- </div>
209
- `;
210
- modal.classList.remove('hidden');
211
- } catch (e) {
212
- console.error('viewUser error', e);
213
- showToast('Failed to load user details', 'error');
214
- }
215
- }
216
-
217
- async function loadDatabaseSchema() {
218
- try {
219
- // no DB backend available in HF simplified; show placeholder
220
- const container = document.getElementById('databaseTab');
221
- if (!container) return;
222
- container.innerHTML = '<div class="p-4 bg-white rounded shadow">No database schema available in file-based HF mode.</div>';
223
- } catch (e) {
224
- console.error(e);
225
- }
226
- }
227
-
228
- async function loadTokenUsage() {
229
- try {
230
- const container = document.getElementById('tokensTab');
231
- if (!container) return;
232
- container.innerHTML = `
233
- <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
234
- <canvas id="providerUsageChart"></canvas>
235
- <canvas id="costBreakdownChart"></canvas>
236
- </div>
237
- `;
238
- // create sample charts
239
- const pctx = document.getElementById('providerUsageChart')?.getContext('2d');
240
- if (pctx) new Chart(pctx, {type:'bar', data:{labels:['Groq','Gemini'], datasets:[{label:'Input Tokens', data:[150000,85000], backgroundColor:['#3b82f6','#9333ea']}]}, options:{responsive:true}});
241
- const cctx = document.getElementById('costBreakdownChart')?.getContext('2d');
242
- if (cctx) new Chart(cctx, {type:'pie', data:{labels:['Conversation','Analysis','Planning'], datasets:[{data:[40,30,30], backgroundColor:['#22c55e','#3b82f6','#f97316']}]}, options:{responsive:true}});
243
- } catch (e) { console.error(e); }
244
- }
245
-
246
- async function loadSystemHealth() {
247
- try {
248
- const res = await fetch('/system/status');
249
- const data = await res.json();
250
- const container = document.getElementById('systemTab');
251
- if (!container) return;
252
- container.innerHTML = `<div class="p-4 bg-white rounded shadow"><pre class="text-sm">${JSON.stringify(data, null, 2)}</pre></div>`;
253
- } catch (e) {
254
- console.error('system health error', e);
255
- }
256
- }
257
-
258
- function showToast(message, type='info'){
259
- const toast = document.createElement('div');
260
- toast.className = `fixed top-4 right-4 p-3 rounded shadow z-50 ${type==='success'?'bg-green-600':type==='error'?'bg-red-600':'bg-blue-600'} text-white`;
261
- toast.textContent = message; document.body.appendChild(toast);
262
- setTimeout(()=>toast.remove(),4000);
263
- }
264
-
265
- // Save data button
266
- document.addEventListener('click', (e) => {
267
- if (e.target && e.target.id === 'saveDataBtn') {
268
- e.target.disabled = true;
269
- e.target.textContent = 'Preparando...';
270
- fetch('/admin/export/all')
271
- .then(res => {
272
- if (!res.ok) throw new Error('Export failed');
273
- return res.blob();
274
- })
275
- .then(blob => {
276
- const url = window.URL.createObjectURL(blob);
277
- const a = document.createElement('a');
278
- a.href = url;
279
- a.download = 'hf_data_export.zip';
280
- document.body.appendChild(a);
281
- a.click();
282
- a.remove();
283
- window.URL.revokeObjectURL(url);
284
- showToast('Download iniciado', 'success');
285
- })
286
- .catch(err => {
287
- console.error('Export all error', err);
288
- showToast('Falha ao exportar dados', 'error');
289
- })
290
- .finally(() => {
291
- e.target.disabled = false;
292
- e.target.textContent = 'Salvar dados';
293
- });
294
- }
295
- });
296
- </script>
297
- {% endblock %}