Download admin.html from YADUUG/eng2pun: direct link, hf CLI and curl.
- Browser
- Download file 8.08 kB
-
https://huggingface.co/spaces/YADUUG/eng2pun/resolve/main/admin.html
- Command line
-
hf download hf://spaces/YADUUG/eng2pun/admin.html
-
curl -L -o admin.html https://huggingface.co/spaces/YADUUG/eng2pun/resolve/main/admin.html
8.08 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>PunjabiDoc Admin (Firebase)</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| </head> | |
| <body class="bg-gray-50 h-screen flex flex-col font-sans"> | |
| <!-- Header --> | |
| <header class="bg-white shadow-sm px-6 py-4 flex justify-between items-center"> | |
| <h1 class="text-xl font-bold text-gray-800">PunjabiDoc <span class="text-red-600">Admin</span></h1> | |
| <button id="logoutBtn" class="text-sm text-gray-500 hover:text-red-500 hidden">Logout</button> | |
| </header> | |
| <!-- Login Container --> | |
| <div id="loginContainer" class="flex-1 flex items-center justify-center p-4"> | |
| <div class="bg-white p-8 rounded-xl shadow-lg w-full max-w-md text-center"> | |
| <h2 class="text-2xl font-bold mb-6">Admin Access</h2> | |
| <button id="googleLoginBtn" | |
| class="w-full flex items-center justify-center space-x-2 bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 font-bold py-3 rounded-lg shadow-sm hover:shadow transition transform active:scale-95"> | |
| <img src="https://www.gstatic.com/firebasejs/ui/2.0.0/images/auth/google.svg" class="w-5 h-5" | |
| alt="Google"> | |
| <span>Sign in with Google</span> | |
| </button> | |
| <p id="errorMsg" class="text-red-500 text-sm mt-4 hidden"></p> | |
| </div> | |
| </div> | |
| <!-- Dashboard Container --> | |
| <div id="dashboardContainer" class="hidden container mx-auto p-6 flex-1"> | |
| <div class="bg-white rounded-xl shadow-sm border border-gray-200 overflow-hidden"> | |
| <div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center"> | |
| <h3 class="text-lg font-semibold text-gray-700">User Management <span id="adminEmail" | |
| class="text-xs text-gray-400 ml-2"></span></h3> | |
| <button id="refreshBtn" class="text-sm text-blue-600 hover:underline">Refresh List</button> | |
| </div> | |
| <table class="w-full text-left"> | |
| <thead class="bg-gray-50 text-gray-600 text-sm uppercase tracking-wider"> | |
| <tr> | |
| <th class="px-6 py-3">User</th> | |
| <th class="px-6 py-3">Credits</th> | |
| <th class="px-6 py-3">Role</th> | |
| <th class="px-6 py-3 text-right">Actions</th> | |
| </tr> | |
| </thead> | |
| <tbody id="userTableBody" class="divide-y divide-gray-200"> | |
| <!-- Users injected here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| </div> | |
| <!-- Firebase SDKs --> | |
| <script type="module"> | |
| import { initializeApp } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-app.js"; | |
| import { getAuth, signInWithPopup, GoogleAuthProvider, onAuthStateChanged, signOut } from "https://www.gstatic.com/firebasejs/10.7.1/firebase-auth.js"; | |
| const firebaseConfig = { | |
| apiKey: "AIzaSyCWzhxW8XDAUmwSHke7oK0AW0gTG5GM73U", | |
| authDomain: "eng2pun-fc01a.firebaseapp.com", | |
| projectId: "eng2pun-fc01a", | |
| storageBucket: "eng2pun-fc01a.firebasestorage.app", | |
| messagingSenderId: "191816044712", | |
| appId: "1:191816044712:web:058fcaf3a64d89a9d0329d", | |
| measurementId: "G-CXXHHJEZV7" | |
| }; | |
| const app = initializeApp(firebaseConfig); | |
| const auth = getAuth(app); | |
| const provider = new GoogleAuthProvider(); | |
| const loginContainer = document.getElementById('loginContainer'); | |
| const dashboardContainer = document.getElementById('dashboardContainer'); | |
| const userTableBody = document.getElementById('userTableBody'); | |
| const logoutBtn = document.getElementById('logoutBtn'); | |
| const googleLoginBtn = document.getElementById('googleLoginBtn'); | |
| const errorMsg = document.getElementById('errorMsg'); | |
| const refreshBtn = document.getElementById('refreshBtn'); | |
| let authToken = null; | |
| googleLoginBtn.addEventListener('click', () => { | |
| signInWithPopup(auth, provider).catch(e => { | |
| errorMsg.innerText = e.message; | |
| errorMsg.classList.remove('hidden'); | |
| }); | |
| }); | |
| logoutBtn.addEventListener('click', () => signOut(auth)); | |
| refreshBtn.addEventListener('click', loadUsers); | |
| onAuthStateChanged(auth, async (user) => { | |
| if (user) { | |
| authToken = "Bearer " + await user.getIdToken(); | |
| document.getElementById('adminEmail').innerText = user.email; | |
| try { | |
| await loadUsers(); // Will fail if not admin | |
| loginContainer.classList.add('hidden'); | |
| dashboardContainer.classList.remove('hidden'); | |
| logoutBtn.classList.remove('hidden'); | |
| errorMsg.classList.add('hidden'); | |
| } catch (e) { | |
| console.error(e); | |
| errorMsg.innerText = "Access Denied: You are not an admin."; | |
| errorMsg.classList.remove('hidden'); | |
| // Optional: signOut(auth) if strict | |
| } | |
| } else { | |
| authToken = null; | |
| dashboardContainer.classList.add('hidden'); | |
| loginContainer.classList.remove('hidden'); | |
| logoutBtn.classList.add('hidden'); | |
| } | |
| }); | |
| async function loadUsers() { | |
| if (!authToken) return; | |
| const res = await fetch('/api/admin/users', { | |
| headers: { 'Authorization': authToken } | |
| }); | |
| if (!res.ok) throw new Error("Unauthorized"); | |
| const users = await res.json(); | |
| renderUsers(users); | |
| } | |
| function renderUsers(users) { | |
| userTableBody.innerHTML = ''; | |
| users.forEach(user => { | |
| const tr = document.createElement('tr'); | |
| tr.innerHTML = ` | |
| <td class="px-6 py-4"> | |
| <div class="text-sm font-medium text-gray-900">${user.email || 'No Email'}</div> | |
| <div class="text-xs text-gray-500 font-mono">${user.id}</div> | |
| </td> | |
| <td class="px-6 py-4 text-sm text-gray-600"> | |
| <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${user.credits > 0 ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'}"> | |
| ${(user.credits || 0).toLocaleString()} | |
| </span> | |
| </td> | |
| <td class="px-6 py-4 text-sm text-gray-500">${user.isAdmin ? 'Admin' : 'User'}</td> | |
| <td class="px-6 py-4 text-right"> | |
| <button onclick="window.addCredits('${user.id}')" class="text-blue-600 hover:text-blue-900 text-sm font-medium">Add Credits</button> | |
| </td> | |
| `; | |
| userTableBody.appendChild(tr); | |
| }); | |
| } | |
| // Attach to window for onclick | |
| window.addCredits = async (userId) => { | |
| const amount = prompt("Enter credits to add:"); | |
| if (!amount) return; | |
| try { | |
| const res = await fetch('/api/admin/add-credits', { | |
| method: 'POST', | |
| headers: { | |
| 'Authorization': authToken, | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ user_id: userId, amount: Number(amount) }) | |
| }); | |
| if (res.ok) { | |
| alert("Success"); | |
| loadUsers(); | |
| } else { | |
| alert("Failed"); | |
| } | |
| } catch (e) { | |
| alert("Error"); | |
| } | |
| }; | |
| </script> | |
| </body> | |
| </html> |