SISTEM INFORMASI BELAJAR DAN BERDATA TPP PASURUAN (BERDAYA)

<!doctype html>
<html lang="id" class="h-full">
 <head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sistem Manajemen TPP</title>
  <script src="https://cdn.tailwindcss.com/3.4.17"></script>
  <script src="https://cdn.jsdelivr.net/npm/lucide@0.263.0/dist/umd/lucide.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.min.js"></script>
  <script src="/_sdk/element_sdk.js"></script>
  <script src="/_sdk/data_sdk.js"></script>
  <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&amp;display=swap" rel="stylesheet">
  <style>
* { font-family: 'Plus Jakarta Sans', sans-serif; }
html, body { height: 100%; margin: 0; }
.app-root { height: 100%; overflow: auto; }
.sidebar-link { transition: all 0.2s; }
.sidebar-link:hover, .sidebar-link.active { background: rgba(255,255,255,0.15); }
.card-hover { transition: transform 0.2s, box-shadow 0.2s; }
.card-hover:hover { transform: translateY(-2px); box-shadow: 0 8px 25px rgba(0,0,0,0.1); }
.bubble-float { animation: floatBubble 3s ease-in-out infinite; }
@keyframes floatBubble { 0%,100%{ transform: translateY(0); } 50%{ transform: translateY(-8px); } }
.fade-in { animation: fadeIn 0.3s ease; }
@keyframes fadeIn { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
.toast-show { animation: toastIn 0.3s ease; }
@keyframes toastIn { from { opacity:0; transform:translateX(100px); } to { opacity:1; transform:translateX(0); } }
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
.chat-bubble-left { border-radius: 4px 16px 16px 16px; }
.chat-bubble-right { border-radius: 16px 4px 16px 16px; }
</style>
  <script>
tailwind.config = {
  theme: {
    extend: {
      colors: {
        brand: { 50:'#eff6ff', 100:'#dbeafe', 200:'#bfdbfe', 300:'#93c5fd', 400:'#60a5fa', 500:'#2563eb', 600:'#1d4ed8', 700:'#1e3a5f', 800:'#172554', 900:'#0f172a' },
        accent: { 400:'#f59e0b', 500:'#d97706' },
        surface: '#f8fafc'
      }
    }
  }
}
</script>
  <style>body { box-sizing: border-box; }</style>
 </head>
 <body class="h-full bg-surface text-brand-900">
  <div id="app" class="app-root"></div>
  <script>
// ==================== STATE ====================
let allData = [];
let currentUser = null; // { username, role, kecamatan, __backendId }
let currentPage = 'login';
let adminPage = 'dashboard';
let userPage = 'belajar';
let toastMsg = '';
let toastTimer = null;
let chatDraft = '';
let onlineUsers = {}; // Track which users are currently online
let lastActivityTime = {}; // Track last activity per user

const defaultConfig = {
  app_title: 'BERDAYA TPP PASURUAN',
  background_color: '#0f172a',
  surface_color: '#f8fafc',
  text_color: '#1e293b',
  primary_color: '#2563eb',
  secondary_color: '#f59e0b'
};

// ==================== HELPERS ====================
function getByType(type) { return allData.filter(d => d.type === type); }
function showToast(msg) {
  toastMsg = msg;
  render();
  clearTimeout(toastTimer);
  toastTimer = setTimeout(() => { toastMsg = ''; render(); }, 3000);
}

function isUserOnline(username) {
  return onlineUsers[username] === true;
}

function updateUserOnlineStatus(username, isOnline) {
  onlineUsers[username] = isOnline;
  if (isOnline) {
    lastActivityTime[username] = Date.now();
  }
  render();
}

function formatDate(iso) {
  if (!iso) return '-';
  const d = new Date(iso);
  return d.toLocaleDateString('id-ID', { day:'2-digit', month:'short', year:'numeric', hour:'2-digit', minute:'2-digit' });
}

// ==================== DATA SDK ====================
const dataHandler = {
  onDataChanged(data) {
    allData = data;
    render();
  }
};

async function initApp() {
  const r = await window.dataSdk.init(dataHandler);
  if (!r.isOk) console.error('Data SDK init failed');
  render();
}

async function createRecord(obj) {
  if (allData.length >= 999) { showToast('Batas data tercapai (999)'); return false; }
  const r = await window.dataSdk.create(obj);
  if (!r.isOk) { showToast('Gagal menyimpan data'); return false; }
  return true;
}
async function updateRecord(obj) {
  const r = await window.dataSdk.update(obj);
  if (!r.isOk) showToast('Gagal update');
  return r.isOk;
}
async function deleteRecord(obj) {
  const r = await window.dataSdk.delete(obj);
  if (!r.isOk) showToast('Gagal hapus');
  return r.isOk;
}

// ==================== ELEMENT SDK ====================
window.elementSdk.init({
  defaultConfig,
  onConfigChange: async (config) => {
    document.documentElement.style.setProperty('--bg', config.background_color || defaultConfig.background_color);
    document.documentElement.style.setProperty('--surface', config.surface_color || defaultConfig.surface_color);
    document.documentElement.style.setProperty('--text', config.text_color || defaultConfig.text_color);
    document.documentElement.style.setProperty('--primary', config.primary_color || defaultConfig.primary_color);
    document.documentElement.style.setProperty('--secondary', config.secondary_color || defaultConfig.secondary_color);
    render();
  },
  mapToCapabilities: (config) => ({
    recolorables: [
      { get: () => config.background_color || defaultConfig.background_color, set: v => { config.background_color = v; window.elementSdk.setConfig({ background_color: v }); } },
      { get: () => config.surface_color || defaultConfig.surface_color, set: v => { config.surface_color = v; window.elementSdk.setConfig({ surface_color: v }); } },
      { get: () => config.text_color || defaultConfig.text_color, set: v => { config.text_color = v; window.elementSdk.setConfig({ text_color: v }); } },
      { get: () => config.primary_color || defaultConfig.primary_color, set: v => { config.primary_color = v; window.elementSdk.setConfig({ primary_color: v }); } },
      { get: () => config.secondary_color || defaultConfig.secondary_color, set: v => { config.secondary_color = v; window.elementSdk.setConfig({ secondary_color: v }); } }
    ],
    borderables: [],
    fontEditable: undefined,
    fontSizeable: undefined
  }),
  mapToEditPanelValues: (config) => new Map([
    ['app_title', config.app_title || defaultConfig.app_title]
  ])
});

// ==================== RENDER ====================
function render() {
  const app = document.getElementById('app');
  if (!app) return;
  const cfg = window.elementSdk.config || defaultConfig;
  const title = cfg.app_title || defaultConfig.app_title;

  if (currentPage === 'login') {
    app.innerHTML = renderLogin(title);
  } else if (currentPage === 'admin') {
    app.innerHTML = renderAdmin(title);
  } else if (currentPage === 'user') {
    app.innerHTML = renderUser(title);
  }
  lucide.createIcons();
  bindEvents();
}

// ==================== LOGIN PAGE ====================
function renderLogin(title) {
  return `
  <div class="h-full flex items-center justify-center" style="background: linear-gradient(135deg, #0f172a 0%, #1e3a5f 50%, #2563eb 100%);">
    <div class="bg-white rounded-2xl shadow-2xl p-8 w-full max-w-md mx-4 fade-in">
      <div class="text-center mb-8">
        <div class="w-16 h-16 bg-brand-500 rounded-2xl flex items-center justify-center mx-auto mb-4">
          <i data-lucide="database" class="text-white" style="width:32px;height:32px;"></i>
        </div>
        <h1 class="text-2xl font-bold text-brand-900">${title}</h1>
        <p class="text-sm text-gray-500 mt-1">Masuk ke sistem untuk melanjutkan</p>
      </div>
      <div id="login-error" class="hidden bg-red-50 text-red-600 text-sm p-3 rounded-lg mb-4"></div>
      <form id="login-form" class="space-y-4">
        <div>
          <label for="login-user" class="block text-sm font-medium text-gray-700 mb-1">Username</label>
          <input id="login-user" type="text" class="w-full px-4 py-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-brand-500 focus:border-transparent outline-none" placeholder="Masukkan username">
        </div>
        <div>
          <label for="login-pass" class="block text-sm font-medium text-gray-700 mb-1">Password</label>
          <input id="login-pass" type="password" class="w-full px-4 py-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-brand-500 focus:border-transparent outline-none" placeholder="Masukkan password">
        </div>
        <button type="submit" class="w-full py-3 bg-brand-500 hover:bg-brand-600 text-white font-semibold rounded-xl transition">Masuk</button>
      </form>
    </div>
  </div>
  ${renderToast()}`;
}

// ==================== ADMIN PAGES ====================
function renderAdmin(title) {
  const pages = {
    dashboard: renderAdminDashboard,
    belajar: renderAdminBelajar,
    berdata: renderAdminBerdata,
    pengguna: renderAdminPengguna,
    aktifitas: renderAdminAktifitas,
    info: renderAdminInfo,
    chat: renderAdminChat
  };
  const content = (pages[adminPage] || renderAdminDashboard)();
  
  const menuItems = [
    { id:'dashboard', icon:'layout-dashboard', label:'Dashboard' },
    { id:'belajar', icon:'book-open', label:'Pusat Belajar' },
    { id:'berdata', icon:'file-spreadsheet', label:'Berdata' },
    { id:'pengguna', icon:'users', label:'Data Pengguna' },
    { id:'aktifitas', icon:'activity', label:'Aktifitas' },
    { id:'info', icon:'megaphone', label:'Info Hot' },
    { id:'chat', icon:'message-circle', label:'Chat' }
  ];

  return `
  <div class="h-full flex">
    <!-- Sidebar -->
    <aside class="w-64 flex-shrink-0 text-white flex flex-col" style="background: linear-gradient(180deg, #0f172a 0%, #1e3a5f 100%);">
      <div class="p-5 border-b border-white/10">
        <div class="flex items-center gap-3">
          <div class="w-10 h-10 bg-brand-500 rounded-xl flex items-center justify-center">
            <i data-lucide="database" class="text-white" style="width:20px;height:20px;"></i>
          </div>
          <div>
            <div class="font-bold text-sm">${title}</div>
            <div class="text-xs text-blue-300">Admin Panel</div>
          </div>
        </div>
      </div>
      <nav class="flex-1 p-3 space-y-1 overflow-auto">
        ${menuItems.map(m => `
          <button data-admin-nav="${m.id}" class="sidebar-link w-full flex items-center gap-3 px-4 py-3 rounded-xl text-sm ${adminPage===m.id?'active bg-white/15 font-semibold':'text-blue-200 hover:text-white'}">
            <i data-lucide="${m.icon}" style="width:18px;height:18px;"></i> ${m.label}
          </button>
        `).join('')}
      </nav>
      <div class="p-3 border-t border-white/10">
        <button id="admin-logout" class="sidebar-link w-full flex items-center gap-3 px-4 py-3 rounded-xl text-sm text-red-300 hover:text-red-200">
          <i data-lucide="log-out" style="width:18px;height:18px;"></i> Keluar
        </button>
      </div>
    </aside>
    <!-- Main -->
    <main class="flex-1 overflow-auto p-6 fade-in">${content}</main>
  </div>
  ${renderToast()}`;
}

function renderAdminDashboard() {
  const users = getByType('user');
  const tagihan = getByType('tagihan');
  const activities = getByType('activity');
  const loggedKec = [...new Set(activities.filter(a=>a.action==='login').map(a=>a.kecamatan))];
  
  return `
    <h2 class="text-2xl font-bold mb-6">Dashboard</h2>
    <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
      ${statCard('users','Total Pengguna', users.length, 'bg-blue-500')}
      ${statCard('file-spreadsheet','Total Tagihan', tagihan.length, 'bg-amber-500')}
      ${statCard('map-pin','Kecamatan Login', loggedKec.length, 'bg-emerald-500')}
      ${statCard('activity','Total Aktifitas', activities.length, 'bg-purple-500')}
    </div>
    <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
      <div class="bg-white rounded-2xl p-6 shadow-sm">
        <h3 class="font-bold text-lg mb-4">Status Kecamatan</h3>
        <div class="space-y-2 max-h-64 overflow-auto">
          ${users.length === 0 ? '<p class="text-gray-400 text-sm">Belum ada pengguna terdaftar</p>' : 
            users.map(u => {
              const logged = loggedKec.includes(u.kecamatan);
              return `<div class="flex items-center justify-between p-3 rounded-xl ${logged?'bg-emerald-50':'bg-gray-50'}">
                <div>
                  <div class="font-medium text-sm">${u.kecamatan || u.username}</div>
                  <div class="text-xs text-gray-500">${u.username}</div>
                </div>
                <span class="text-xs px-3 py-1 rounded-full font-medium ${logged?'bg-emerald-100 text-emerald-700':'bg-gray-200 text-gray-500'}">${logged?'Sudah Login':'Belum Login'}</span>
              </div>`;
            }).join('')}
        </div>
      </div>
      <div class="bg-white rounded-2xl p-6 shadow-sm">
        <h3 class="font-bold text-lg mb-4">Aktifitas Terbaru</h3>
        <div class="space-y-2 max-h-64 overflow-auto">
          ${activities.length === 0 ? '<p class="text-gray-400 text-sm">Belum ada aktifitas</p>' :
            activities.slice(-10).reverse().map(a => `
              <div class="flex items-center gap-3 p-3 bg-gray-50 rounded-xl">
                <div class="w-8 h-8 bg-brand-100 rounded-lg flex items-center justify-center"><i data-lucide="user" style="width:14px;height:14px;" class="text-brand-500"></i></div>
                <div class="flex-1 min-w-0">
                  <div class="text-sm font-medium truncate">${a.username} - ${a.action}</div>
                  <div class="text-xs text-gray-400">${formatDate(a.timestamp)}</div>
                </div>
              </div>
            `).join('')}
        </div>
      </div>
    </div>`;
}

function statCard(icon, label, value, bg) {
  return `<div class="card-hover bg-white rounded-2xl p-5 shadow-sm">
    <div class="flex items-center gap-4">
      <div class="w-12 h-12 ${bg} rounded-xl flex items-center justify-center"><i data-lucide="${icon}" class="text-white" style="width:22px;height:22px;"></i></div>
      <div><div class="text-2xl font-bold">${value}</div><div class="text-xs text-gray-500">${label}</div></div>
    </div>
  </div>`;
}

function renderAdminBelajar() {
  const materials = getByType('materi');
  return `
    <h2 class="text-2xl font-bold mb-6">Pusat Belajar</h2>
    <div class="bg-white rounded-2xl p-6 shadow-sm mb-6">
      <h3 class="font-semibold mb-4">Tambah Materi</h3>
      <form id="form-materi" class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <input id="mat-title" placeholder="Judul Materi" class="px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500" required>
        <select id="mat-type" class="px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500">
          <option value="pdf">PDF</option><option value="ppt">PPT</option><option value="video">Video/YouTube</option>
        </select>
        <input id="mat-category" placeholder="Kategori" class="px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500">
        <input id="mat-link" placeholder="Link Materi (URL)" class="px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500">
        <textarea id="mat-summary" placeholder="Ringkasan Materi" rows="3" class="md:col-span-2 px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500"></textarea>
        <div class="md:col-span-2"><button type="submit" class="px-6 py-3 bg-brand-500 hover:bg-brand-600 text-white rounded-xl font-semibold transition">Simpan Materi</button></div>
      </form>
    </div>
    <div class="bg-white rounded-2xl p-6 shadow-sm">
      <h3 class="font-semibold mb-4">Daftar Materi (${materials.length})</h3>
      ${materials.length===0?'<p class="text-gray-400 text-sm">Belum ada materi</p>':`
      <div class="overflow-auto">
        <table class="w-full text-sm">
          <thead><tr class="border-b text-left text-gray-500"><th class="p-3">No</th><th class="p-3">Judul</th><th class="p-3">Tipe</th><th class="p-3">Kategori</th><th class="p-3">Aksi</th></tr></thead>
          <tbody>${materials.map((m,i)=>`
            <tr class="border-b hover:bg-gray-50">
              <td class="p-3">${i+1}</td>
              <td class="p-3 font-medium">${m.title}</td>
              <td class="p-3"><span class="px-2 py-1 rounded-full text-xs font-medium ${m.file_type==='video'?'bg-red-100 text-red-600':m.file_type==='ppt'?'bg-orange-100 text-orange-600':'bg-blue-100 text-blue-600'}">${(m.file_type||'').toUpperCase()}</span></td>
              <td class="p-3">${m.category||'-'}</td>
              <td class="p-3"><button data-del-materi="${m.__backendId}" class="text-red-500 hover:text-red-700 text-xs font-medium">Hapus</button></td>
            </tr>
          `).join('')}</tbody>
        </table>
      </div>`}
    </div>`;
}

function renderAdminBerdata() {
  const tagihan = getByType('tagihan');
  return `
    <h2 class="text-2xl font-bold mb-6">Berdata - Tagihan</h2>
    <div class="bg-white rounded-2xl p-6 shadow-sm mb-6">
      <h3 class="font-semibold mb-4">Tambah Tagihan Data</h3>
      <form id="form-tagihan" class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <input id="tag-title" placeholder="Judul Tagihan" class="px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500" required>
        <input id="tag-link" placeholder="Link Google Sheet" class="px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500" required>
        <input id="tag-deadline" type="date" class="px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500">
        <input id="tag-status" placeholder="Status (aktif/selesai)" value="aktif" class="px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500">
        <textarea id="tag-desc" placeholder="Penjelasan pengisian data" rows="2" class="md:col-span-2 px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500"></textarea>
        <div class="md:col-span-2"><button type="submit" class="px-6 py-3 bg-brand-500 hover:bg-brand-600 text-white rounded-xl font-semibold transition">Simpan Tagihan</button></div>
      </form>
    </div>
    <div class="bg-white rounded-2xl p-6 shadow-sm">
      <h3 class="font-semibold mb-4">Daftar Tagihan (${tagihan.length})</h3>
      ${tagihan.length===0?'<p class="text-gray-400 text-sm">Belum ada tagihan</p>':`
      <div class="space-y-3">
        ${tagihan.map(t=>`
          <div class="flex items-center justify-between p-4 border rounded-xl hover:bg-gray-50">
            <div class="flex-1 min-w-0">
              <div class="font-medium">${t.title}</div>
              <div class="text-xs text-gray-500 mt-1">Deadline: ${t.deadline||'-'} | Status: ${t.status||'aktif'}</div>
              <div class="text-xs text-gray-400 truncate mt-1">${t.link||''}</div>
            </div>
            <button data-del-tagihan="${t.__backendId}" class="ml-3 text-red-500 hover:text-red-700"><i data-lucide="trash-2" style="width:16px;height:16px;"></i></button>
          </div>
        `).join('')}
      </div>`}
    </div>`;
}

function renderAdminPengguna() {
  const users = getByType('user');
  return `
    <h2 class="text-2xl font-bold mb-6">Data Pengguna</h2>
    <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
      <!-- Form Manual -->
      <div class="bg-white rounded-2xl p-6 shadow-sm">
        <h3 class="font-semibold mb-4">Tambah Manual</h3>
        <form id="form-user" class="space-y-4">
          <input id="usr-name" placeholder="Username" class="w-full px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500" required>
          <input id="usr-pass" placeholder="Password" class="w-full px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500" required>
          <input id="usr-kec" placeholder="Kecamatan" class="w-full px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500" required>
          <button type="submit" class="w-full px-6 py-3 bg-brand-500 hover:bg-brand-600 text-white rounded-xl font-semibold transition">Tambah</button>
        </form>
      </div>
      <!-- Import Excel -->
      <div class="bg-white rounded-2xl p-6 shadow-sm">
        <h3 class="font-semibold mb-4">Import Excel</h3>
        <div class="space-y-3">
          <div>
            <label class="block text-sm font-medium text-gray-700 mb-2">Pilih File</label>
            <input id="import-excel" type="file" accept=".xlsx,.xls" class="w-full px-4 py-3 border rounded-xl text-sm cursor-pointer">
          </div>
          <button id="btn-import" type="button" class="w-full px-6 py-3 bg-emerald-500 hover:bg-emerald-600 text-white rounded-xl font-semibold transition flex items-center justify-center gap-2">
            <i data-lucide="upload" style="width:16px;height:16px;"></i> Import
          </button>
          <div id="import-status" class="hidden text-sm p-3 rounded-lg"></div>
        </div>
      </div>
      <!-- Paste Data -->
      <div class="bg-white rounded-2xl p-6 shadow-sm">
        <h3 class="font-semibold mb-4">Paste Data 📋</h3>
        <div class="space-y-3">
          <p class="text-xs text-gray-600 mb-2">Paste dari spreadsheet (3 kolom: Username | Password | Kecamatan)</p>
          <textarea id="paste-data" placeholder="Paste data di sini&#10;user1	pass123	Pasuruan&#10;user2	pass456	Pandaan" rows="4" class="w-full px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500 text-sm font-mono"></textarea>
          <button id="btn-paste" type="button" class="w-full px-6 py-3 bg-violet-500 hover:bg-violet-600 text-white rounded-xl font-semibold transition flex items-center justify-center gap-2">
            <i data-lucide="clipboard-paste" style="width:16px;height:16px;"></i> Paste
          </button>
          <div id="paste-status" class="hidden text-sm p-3 rounded-lg"></div>
        </div>
      </div>
    </div>
    <div class="bg-white rounded-2xl p-6 shadow-sm">
      <h3 class="font-semibold mb-4">Daftar Pengguna (${users.length})</h3>
      ${users.length===0?'<p class="text-gray-400 text-sm">Belum ada pengguna</p>':`
      <div class="overflow-auto">
        <table class="w-full text-sm">
          <thead><tr class="border-b text-left text-gray-500"><th class="p-3">No</th><th class="p-3">Username</th><th class="p-3">Kecamatan</th><th class="p-3">Aksi</th></tr></thead>
          <tbody>${users.map((u,i)=>`
            <tr class="border-b hover:bg-gray-50">
              <td class="p-3">${i+1}</td>
              <td class="p-3 font-medium">${u.username}</td>
              <td class="p-3">${u.kecamatan||'-'}</td>
              <td class="p-3"><button data-del-user="${u.__backendId}" class="text-red-500 hover:text-red-700 text-xs font-medium">Hapus</button></td>
            </tr>
          `).join('')}</tbody>
        </table>
      </div>`}
    </div>`;
}

function renderAdminAktifitas() {
  const activities = getByType('activity').slice().reverse();
  return `
    <h2 class="text-2xl font-bold mb-6">Aktifitas Pengguna</h2>
    <div class="bg-white rounded-2xl p-6 shadow-sm">
      ${activities.length===0?'<p class="text-gray-400 text-sm">Belum ada aktifitas</p>':`
      <div class="overflow-auto">
        <table class="w-full text-sm">
          <thead><tr class="border-b text-left text-gray-500"><th class="p-3">Waktu</th><th class="p-3">Username</th><th class="p-3">Kecamatan</th><th class="p-3">Aktifitas</th><th class="p-3">Detail</th></tr></thead>
          <tbody>${activities.map(a=>`
            <tr class="border-b hover:bg-gray-50">
              <td class="p-3 text-xs">${formatDate(a.timestamp)}</td>
              <td class="p-3 font-medium">${a.username}</td>
              <td class="p-3">${a.kecamatan||'-'}</td>
              <td class="p-3"><span class="px-2 py-1 rounded-full text-xs font-medium ${a.action==='login'?'bg-green-100 text-green-700':'bg-blue-100 text-blue-700'}">${a.action}</span></td>
              <td class="p-3 text-xs text-gray-500">${a.description||'-'}</td>
            </tr>
          `).join('')}</tbody>
        </table>
      </div>`}
    </div>`;
}

function renderAdminInfo() {
  const infos = getByType('info');
  return `
    <h2 class="text-2xl font-bold mb-6">Info Hot 🔥</h2>
    <div class="bg-white rounded-2xl p-6 shadow-sm mb-6">
      <h3 class="font-semibold mb-4">Tambah Info</h3>
      <form id="form-info" class="space-y-4">
        <input id="info-title" placeholder="Judul Info" class="w-full px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500" required>
        <textarea id="info-content" placeholder="Isi Info" rows="3" class="w-full px-4 py-3 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500" required></textarea>
        <button type="submit" class="px-6 py-3 bg-amber-500 hover:bg-amber-600 text-white rounded-xl font-semibold transition">Tambah Info Hot</button>
      </form>
    </div>
    <div class="bg-white rounded-2xl p-6 shadow-sm">
      <h3 class="font-semibold mb-4">Daftar Info (${infos.length})</h3>
      <div class="space-y-3">
        ${infos.length===0?'<p class="text-gray-400 text-sm">Belum ada info</p>':
          infos.map(info=>`
            <div class="p-4 border-l-4 border-amber-400 bg-amber-50 rounded-xl flex justify-between items-start">
              <div><div class="font-semibold text-sm">${info.title}</div><div class="text-xs text-gray-600 mt-1">${info.content}</div></div>
              <button data-del-info="${info.__backendId}" class="text-red-500 hover:text-red-700 ml-3"><i data-lucide="trash-2" style="width:16px;height:16px;"></i></button>
            </div>
          `).join('')}
      </div>
    </div>`;
}

function renderAdminChat() {
  const users = getByType('user');
  const chats = getByType('chat');
  
  // Get unread chat count per user (messages from user to admin not yet marked as read)
  const getUnreadCount = (username) => {
    return chats.filter(c => 
      c.sender === username && c.receiver === 'admin' && !c.read
    ).length;
  };
  
  return `
    <h2 class="text-2xl font-bold mb-6">Chat</h2>
    <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
      <div class="bg-white rounded-2xl p-4 shadow-sm">
        <h3 class="font-semibold mb-3 text-sm">Daftar Pengguna</h3>
        <div class="space-y-2">
          ${users.length===0?'<p class="text-gray-400 text-xs">Belum ada pengguna terdaftar</p>':
            users.map(u=>{
              const unreadCount = getUnreadCount(u.username);
              const online = isUserOnline(u.username);
              return `<button data-chat-select="${u.username}" class="w-full text-left p-3 rounded-xl hover:bg-brand-50 text-sm font-medium flex items-center gap-2 transition border border-gray-200 relative">
                <div class="relative">
                  <i data-lucide="message-circle" style="width:14px;height:14px;" class="text-brand-500"></i>
                  <div class="absolute -top-1 -right-1 w-2.5 h-2.5 rounded-full ${online ? 'bg-green-500 animate-pulse' : 'bg-gray-300'}"></div>
                </div>
                <div class="flex-1">
                  <div class="flex items-center gap-1">
                    ${u.username}
                    ${online ? '<span class="text-xs px-2 py-0.5 bg-green-100 text-green-700 rounded-full font-semibold">Online</span>' : ''}
                  </div>
                  <div class="text-xs text-gray-400">${u.kecamatan||'N/A'}</div>
                </div>
                ${unreadCount > 0 ? `<span class="absolute top-1 right-1 w-6 h-6 bg-red-500 text-white text-xs flex items-center justify-center rounded-full font-bold animate-pulse">${unreadCount}</span>` : ''}
              </button>`;
            }).join('')}
        </div>
      </div>
      <div class="lg:col-span-2 bg-white rounded-2xl p-4 shadow-sm flex flex-col" id="chat-area" style="height: 600px;">
        <p class="text-gray-400 text-sm text-center py-12">Pilih pengguna untuk memulai chat</p>
      </div>
    </div>`;
}

function renderChatWith(partner) {
  const chats = getByType('chat').filter(c =>
    (c.sender===partner && c.receiver==='admin') || (c.sender==='admin' && c.receiver===partner)
  ).sort((a,b) => new Date(a.timestamp) - new Date(b.timestamp));
  
  // Mark messages from partner as read
  (async () => {
    for (let c of chats) {
      if (c.sender === partner && c.receiver === 'admin' && !c.read) {
        const updated = { ...c, read: true };
        await updateRecord(updated);
      }
    }
  })();
  
  const area = document.getElementById('chat-area');
  if (!area) return;
  area.innerHTML = `
    <h3 class="font-semibold mb-3 text-sm border-b pb-3 flex-shrink-0">Chat dengan ${partner}</h3>
    <div class="flex-1 overflow-auto space-y-3 mb-3 p-2" id="chat-messages">
      ${chats.map(c => c.sender==='admin' ? 
        `<div class="flex justify-end"><div class="chat-bubble-right bg-brand-500 text-white px-4 py-2 text-sm max-w-xs rounded-lg">${c.message}<div class="text-xs opacity-70 mt-1">${formatDate(c.timestamp)}</div></div></div>` :
        `<div class="flex justify-start"><div class="chat-bubble-left bg-gray-100 px-4 py-2 text-sm max-w-xs rounded-lg">${c.message}<div class="text-xs text-gray-400 mt-1">${formatDate(c.timestamp)}</div></div></div>`
      ).join('')}
    </div>
    <form id="admin-chat-form" data-partner="${partner}" class="flex gap-2 flex-shrink-0">
      <input id="admin-chat-input" placeholder="Ketik pesan..." class="flex-1 px-4 py-2 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500 text-sm" required>
      <button type="submit" class="px-4 py-2 bg-brand-500 text-white rounded-xl hover:bg-brand-600 transition font-medium"><i data-lucide="send" style="width:16px;height:16px;"></i></button>
    </form>`;
  lucide.createIcons();
  const msgs = document.getElementById('chat-messages');
  if (msgs) msgs.scrollTop = msgs.scrollHeight;
  
  // Rebind admin chat form
  const acf = document.getElementById('admin-chat-form');
  if (acf) acf.onsubmit = async (e) => {
    e.preventDefault();
    const input = document.getElementById('admin-chat-input');
    const msg = input.value.trim();
    if (!msg) return;
    input.value = '';
    const btn = acf.querySelector('button[type=submit]');
    btn.disabled = true;
    btn.innerHTML = '...';
    const created = await createRecord({
      type: 'chat', sender: 'admin', receiver: partner, message: msg, timestamp: new Date().toISOString()
    });
    if (created) {
      setTimeout(() => {
        renderChatWith(partner);
        btn.disabled = false;
        btn.innerHTML = '<i data-lucide="send" style="width:16px;height:16px;"></i>';
        lucide.createIcons();
      }, 300);
    } else {
      btn.disabled = false;
      btn.innerHTML = '<i data-lucide="send" style="width:16px;height:16px;"></i>';
    }
  };
}

// ==================== USER PAGES ====================
function renderUser(title) {
  const pages = {
    belajar: renderUserBelajar,
    berdata: renderUserBerdata,
    info: renderUserInfo,
    masukan: renderUserMasukan
  };
  const content = (pages[userPage] || renderUserBelajar)();
  const infos = getByType('info');
  
  const menuItems = [
    { id:'belajar', icon:'book-open', label:'Pusat Belajar' },
    { id:'berdata', icon:'file-spreadsheet', label:'Berdata' },
    { id:'info', icon:'megaphone', label:'Info' },
    { id:'masukan', icon:'message-circle', label:'Masukan' }
  ];

  return `
  <div class="h-full flex">
    <aside class="w-60 flex-shrink-0 text-white flex flex-col" style="background: linear-gradient(180deg, #1e3a5f 0%, #2563eb 100%);">
      <div class="p-5 border-b border-white/10">
        <div class="flex items-center gap-3">
          <div class="w-10 h-10 bg-white/20 rounded-xl flex items-center justify-center">
            <i data-lucide="user" class="text-white" style="width:20px;height:20px;"></i>
          </div>
          <div>
            <div class="font-bold text-sm">${currentUser?.username||''}</div>
            <div class="text-xs text-blue-200">${currentUser?.kecamatan||''}</div>
          </div>
        </div>
      </div>
      <nav class="flex-1 p-3 space-y-1 overflow-auto">
        ${menuItems.map(m => {
          let badge = '';
          if (m.id === 'masukan') {
            const unreadChats = getByType('chat').filter(c => c.sender === 'admin' && c.receiver === currentUser?.username && !c.read).length;
            if (unreadChats > 0) {
              badge = `<span class="absolute top-2 right-2 w-5 h-5 bg-red-500 text-white text-xs flex items-center justify-center rounded-full font-bold animate-pulse">${unreadChats}</span>`;
            }
          }
          return `
          <button data-user-nav="${m.id}" class="sidebar-link w-full flex items-center gap-3 px-4 py-3 rounded-xl text-sm ${userPage===m.id?'active bg-white/15 font-semibold':'text-blue-200 hover:text-white'} relative">
            <i data-lucide="${m.icon}" style="width:18px;height:18px;"></i> ${m.label}
            ${badge}
          </button>`;
        }).join('')}
      </nav>
      <div class="p-3 border-t border-white/10">
        <button id="user-logout" class="sidebar-link w-full flex items-center gap-3 px-4 py-3 rounded-xl text-sm text-red-300 hover:text-red-200">
          <i data-lucide="log-out" style="width:18px;height:18px;"></i> Keluar
        </button>
      </div>
    </aside>
    <main class="flex-1 overflow-auto p-6 fade-in relative">
      ${content}
      ${infos.length > 0 ? `
        <div class="fixed bottom-6 right-6 bubble-float cursor-pointer z-50" id="info-bubble">
          <div class="w-14 h-14 bg-amber-500 rounded-full flex items-center justify-center shadow-lg relative">
            <i data-lucide="bell" class="text-white" style="width:24px;height:24px;"></i>
            <span class="absolute -top-1 -right-1 w-5 h-5 bg-red-500 rounded-full text-white text-xs flex items-center justify-center font-bold">${infos.length}</span>
          </div>
        </div>
        <div id="info-popup" class="hidden fixed bottom-24 right-6 w-80 bg-white rounded-2xl shadow-2xl border p-4 z-50 max-h-72 overflow-auto">
          <h4 class="font-bold text-sm mb-3 flex items-center gap-2">🔥 Info Terbaru</h4>
          <div class="space-y-2">
            ${infos.map(info=>`<div class="p-3 bg-amber-50 border-l-4 border-amber-400 rounded-lg"><div class="font-semibold text-xs">${info.title}</div><div class="text-xs text-gray-600 mt-1">${info.content}</div></div>`).join('')}
          </div>
        </div>` : ''}
    </main>
  </div>
  ${renderToast()}`;
}

function renderUserBelajar() {
  const materials = getByType('materi');
  return `
    <h2 class="text-2xl font-bold mb-6">Pusat Belajar 📚</h2>
    <div class="bg-white rounded-2xl p-6 shadow-sm">
      ${materials.length===0?'<p class="text-gray-400 text-sm">Belum ada materi tersedia</p>':`
      <div class="overflow-auto">
        <table class="w-full text-sm">
          <thead><tr class="border-b text-left text-gray-500"><th class="p-3">No</th><th class="p-3">Materi</th><th class="p-3">Tipe</th><th class="p-3">Ringkasan</th><th class="p-3">Aksi</th></tr></thead>
          <tbody>${materials.map((m,i)=>`
            <tr class="border-b hover:bg-gray-50">
              <td class="p-3">${i+1}</td>
              <td class="p-3 font-medium">${m.title}</td>
              <td class="p-3"><span class="px-2 py-1 rounded-full text-xs font-medium ${m.file_type==='video'?'bg-red-100 text-red-600':m.file_type==='ppt'?'bg-orange-100 text-orange-600':'bg-blue-100 text-blue-600'}">${(m.file_type||'').toUpperCase()}</span></td>
              <td class="p-3 text-xs text-gray-600 max-w-xs truncate">${m.summary||'-'}</td>
              <td class="p-3">${m.link?`<a href="${m.link}" target="_blank" rel="noopener noreferrer" class="text-brand-500 hover:underline text-xs font-medium" data-log-belajar="${m.title}">Buka</a>`:'-'}</td>
            </tr>
          `).join('')}</tbody>
        </table>
      </div>`}
    </div>`;
}

function renderUserBerdata() {
  const tagihan = getByType('tagihan');
  const kec = currentUser?.kecamatan || '';
  return `
    <h2 class="text-2xl font-bold mb-2">Berdata 📊</h2>
    <p class="text-sm text-gray-500 mb-6">Kecamatan: <strong>${kec}</strong></p>
    <div class="space-y-4">
      ${tagihan.length===0?'<div class="bg-white rounded-2xl p-8 shadow-sm text-center text-gray-400">Belum ada tagihan data</div>':
        tagihan.map(t=>{
          const isExpired = t.deadline && new Date(t.deadline) < new Date();
          return `
          <div class="card-hover bg-white rounded-2xl p-6 shadow-sm border-l-4 ${isExpired?'border-red-400':'border-brand-500'}">
            <div class="flex items-start justify-between">
              <div class="flex-1">
                <h3 class="font-bold text-lg">${t.title}</h3>
                <p class="text-sm text-gray-500 mt-1">${t.description||'Tidak ada deskripsi'}</p>
                <div class="flex items-center gap-4 mt-3">
                  <span class="text-xs px-3 py-1 rounded-full ${isExpired?'bg-red-100 text-red-600':'bg-green-100 text-green-600'} font-medium">
                    Deadline: ${t.deadline||'Tidak ditentukan'}
                  </span>
                  <span class="text-xs px-3 py-1 rounded-full bg-blue-100 text-blue-600 font-medium">${t.status||'aktif'}</span>
                </div>
              </div>
              ${t.link?`<a href="${t.link}" target="_blank" rel="noopener noreferrer" class="px-5 py-2 bg-brand-500 hover:bg-brand-600 text-white rounded-xl text-sm font-semibold transition flex-shrink-0" data-log-berdata="${t.title}">Kerjakan</a>`:''}
            </div>
            <p class="text-xs text-gray-400 mt-3">💡 Data akan terfilter otomatis untuk kecamatan <strong>${kec}</strong></p>
          </div>`;
        }).join('')}
    </div>`;
}

function renderUserInfo() {
  const infos = getByType('info');
  return `
    <h2 class="text-2xl font-bold mb-6">Info 📢</h2>
    <div class="space-y-4">
      ${infos.length===0?'<div class="bg-white rounded-2xl p-8 shadow-sm text-center text-gray-400">Belum ada info</div>':
        infos.map(info=>`
          <div class="card-hover bg-white rounded-2xl p-6 shadow-sm border-l-4 border-amber-400">
            <div class="flex items-start gap-3">
              <div class="text-2xl">🔥</div>
              <div>
                <h3 class="font-bold">${info.title}</h3>
                <p class="text-sm text-gray-600 mt-2">${info.content}</p>
                <p class="text-xs text-gray-400 mt-2">${formatDate(info.timestamp)}</p>
              </div>
            </div>
          </div>
        `).join('')}
    </div>`;
}

function renderUserMasukan() {
  const chats = getByType('chat').filter(c =>
    (c.sender===currentUser?.username && c.receiver==='admin') || (c.sender==='admin' && c.receiver===currentUser?.username)
  ).sort((a,b) => new Date(a.timestamp) - new Date(b.timestamp));
  
  // Count unread messages from admin
  const unreadCount = chats.filter(c => c.sender === 'admin' && c.receiver === currentUser?.username && !c.read).length;
  
  // Mark messages from admin as read
  (async () => {
    for (let c of chats) {
      if (c.sender === 'admin' && c.receiver === currentUser?.username && !c.read) {
        const updated = { ...c, read: true };
        await updateRecord(updated);
      }
    }
  })();
  
  return `
    <h2 class="text-2xl font-bold mb-6 flex items-center gap-2">Masukan & Diskusi 💬 ${unreadCount > 0 ? `<span class="inline-flex items-center justify-center w-6 h-6 bg-red-500 text-white text-xs font-bold rounded-full">${unreadCount}</span>` : ''}</h2>
    <div class="bg-white rounded-2xl shadow-sm overflow-hidden">
      <div class="p-4 border-b bg-brand-50">
        <div class="font-semibold text-sm flex items-center gap-2">
          <div class="relative">
            <i data-lucide="message-circle" style="width:16px;height:16px;" class="text-brand-500"></i>
            <div class="absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full ${isUserOnline('admin') ? 'bg-green-500 animate-pulse' : 'bg-gray-300'}"></div>
          </div>
          Chat dengan Admin ${isUserOnline('admin') ? '<span class="text-xs ml-auto px-2 py-0.5 bg-green-100 text-green-700 rounded-full font-semibold">Online</span>' : '<span class="text-xs ml-auto px-2 py-0.5 bg-gray-100 text-gray-600 rounded-full">Offline</span>'}
        </div>
      </div>
      <div class="h-72 overflow-auto p-4 space-y-2" id="user-chat-messages">
        ${chats.length===0?'<p class="text-gray-400 text-sm text-center py-8">Belum ada pesan. Mulai diskusi!</p>':
          chats.map(c => c.sender===currentUser?.username ?
            `<div class="flex justify-end"><div class="chat-bubble-right bg-brand-500 text-white px-4 py-2 text-sm max-w-xs">${c.message}<div class="text-xs opacity-70 mt-1">${formatDate(c.timestamp)}</div></div></div>` :
            `<div class="flex justify-start"><div class="chat-bubble-left bg-gray-100 px-4 py-2 text-sm max-w-xs">${c.message}<div class="text-xs text-gray-400 mt-1">${formatDate(c.timestamp)}</div></div></div>`
          ).join('')}
      </div>
      <form id="user-chat-form" class="p-4 border-t flex gap-2">
        <input id="user-chat-input" placeholder="Ketik pesan..." class="flex-1 px-4 py-2 border rounded-xl outline-none focus:ring-2 focus:ring-brand-500 text-sm">
        <button type="submit" class="px-4 py-2 bg-brand-500 text-white rounded-xl hover:bg-brand-600 transition"><i data-lucide="send" style="width:16px;height:16px;"></i></button>
      </form>
    </div>`;
}

function renderToast() {
  if (!toastMsg) return '';
  return `<div class="fixed top-4 right-4 z-50 toast-show bg-brand-900 text-white px-5 py-3 rounded-xl shadow-lg text-sm font-medium">${toastMsg}</div>`;
}

// ==================== EVENTS ====================
function bindEvents() {
  // Login
  const loginForm = document.getElementById('login-form');
  if (loginForm) loginForm.onsubmit = async (e) => {
    e.preventDefault();
    const u = document.getElementById('login-user').value.trim();
    const p = document.getElementById('login-pass').value.trim();
    if (!u || !p) return;
    
    // Check admin
    if (u === 'admin' && p === 'admin123') {
      currentUser = { username: 'admin', role: 'admin', kecamatan: 'ALL' };
      currentPage = 'admin';
      updateUserOnlineStatus('admin', true);
      await createRecord({ type:'activity', username:'admin', kecamatan:'ALL', action:'login', description:'Admin login', timestamp: new Date().toISOString() });
      render();
      return;
    }
    
    // Check users
    const users = getByType('user');
    const found = users.find(x => x.username === u && x.password === p);
    if (found) {
      currentUser = found;
      currentPage = 'user';
      updateUserOnlineStatus(found.username, true);
      await createRecord({ type:'activity', username:found.username, kecamatan:found.kecamatan||'', action:'login', description:'User login', timestamp: new Date().toISOString() });
      render();
    } else {
      const err = document.getElementById('login-error');
      if (err) { err.textContent = 'Username atau password salah!'; err.classList.remove('hidden'); }
    }
  };

  // Admin nav
  document.querySelectorAll('[data-admin-nav]').forEach(btn => {
    btn.onclick = () => { adminPage = btn.dataset.adminNav; render(); };
  });
  
  // User nav
  document.querySelectorAll('[data-user-nav]').forEach(btn => {
    btn.onclick = () => { userPage = btn.dataset.userNav; render(); };
  });

  // Logouts
  const al = document.getElementById('admin-logout');
  if (al) al.onclick = () => { 
    updateUserOnlineStatus(currentUser?.username, false);
    currentUser = null; 
    currentPage = 'login'; 
    render(); 
  };
  const ul = document.getElementById('user-logout');
  if (ul) ul.onclick = () => { 
    updateUserOnlineStatus(currentUser?.username, false);
    currentUser = null; 
    currentPage = 'login'; 
    render(); 
  };

  // Admin forms
  const matForm = document.getElementById('form-materi');
  if (matForm) matForm.onsubmit = async (e) => {
    e.preventDefault();
    const btn = matForm.querySelector('button[type=submit]');
    btn.disabled = true; btn.textContent = 'Menyimpan...';
    await createRecord({
      type: 'materi',
      title: document.getElementById('mat-title').value,
      file_type: document.getElementById('mat-type').value,
      category: document.getElementById('mat-category').value,
      link: document.getElementById('mat-link').value,
      summary: document.getElementById('mat-summary').value,
      timestamp: new Date().toISOString()
    });
    showToast('Materi berhasil disimpan');
  };

  const tagForm = document.getElementById('form-tagihan');
  if (tagForm) tagForm.onsubmit = async (e) => {
    e.preventDefault();
    const btn = tagForm.querySelector('button[type=submit]');
    btn.disabled = true; btn.textContent = 'Menyimpan...';
    await createRecord({
      type: 'tagihan',
      title: document.getElementById('tag-title').value,
      link: document.getElementById('tag-link').value,
      deadline: document.getElementById('tag-deadline').value,
      status: document.getElementById('tag-status').value,
      description: document.getElementById('tag-desc').value,
      timestamp: new Date().toISOString()
    });
    showToast('Tagihan berhasil disimpan');
  };

  const usrForm = document.getElementById('form-user');
  if (usrForm) usrForm.onsubmit = async (e) => {
    e.preventDefault();
    const btn = usrForm.querySelector('button[type=submit]');
    btn.disabled = true; btn.textContent = 'Menyimpan...';
    await createRecord({
      type: 'user',
      username: document.getElementById('usr-name').value,
      password: document.getElementById('usr-pass').value,
      kecamatan: document.getElementById('usr-kec').value,
      role: 'user',
      timestamp: new Date().toISOString()
    });
    showToast('Pengguna berhasil ditambahkan');
  };

  const infoForm = document.getElementById('form-info');
  if (infoForm) infoForm.onsubmit = async (e) => {
    e.preventDefault();
    const btn = infoForm.querySelector('button[type=submit]');
    btn.disabled = true; btn.textContent = 'Menyimpan...';
    await createRecord({
      type: 'info',
      title: document.getElementById('info-title').value,
      content: document.getElementById('info-content').value,
      is_hot: true,
      timestamp: new Date().toISOString()
    });
    showToast('Info berhasil ditambahkan');
  };

  // Delete buttons
  document.querySelectorAll('[data-del-materi]').forEach(btn => {
    btn.onclick = async () => {
      const rec = allData.find(d => d.__backendId === btn.dataset.delMateri);
      if (rec) { btn.textContent = '...'; await deleteRecord(rec); showToast('Materi dihapus'); }
    };
  });
  document.querySelectorAll('[data-del-tagihan]').forEach(btn => {
    btn.onclick = async () => {
      const rec = allData.find(d => d.__backendId === btn.dataset.delTagihan);
      if (rec) { await deleteRecord(rec); showToast('Tagihan dihapus'); }
    };
  });
  document.querySelectorAll('[data-del-user]').forEach(btn => {
    btn.onclick = async () => {
      const rec = allData.find(d => d.__backendId === btn.dataset.delUser);
      if (rec) { await deleteRecord(rec); showToast('Pengguna dihapus'); }
    };
  });
  document.querySelectorAll('[data-del-info]').forEach(btn => {
    btn.onclick = async () => {
      const rec = allData.find(d => d.__backendId === btn.dataset.delInfo);
      if (rec) { await deleteRecord(rec); showToast('Info dihapus'); }
    };
  });

  // Template download - REMOVED
  
  // Import Excel
  const btnImport = document.getElementById('btn-import');
  if (btnImport) {
    btnImport.onclick = () => importExcel();
  }

  // Paste Data
  const btnPaste = document.getElementById('btn-paste');
  if (btnPaste) {
    btnPaste.onclick = () => pasteUserData();
  }

  // Chat select (admin)
  document.querySelectorAll('[data-chat-select]').forEach(btn => {
    btn.onclick = () => renderChatWith(btn.dataset.chatSelect);
  });

  // User chat send
  const ucf = document.getElementById('user-chat-form');
  if (ucf) ucf.onsubmit = async (e) => {
    e.preventDefault();
    const input = document.getElementById('user-chat-input');
    const msg = input.value.trim();
    if (!msg) return;
    const btn = ucf.querySelector('button[type=submit]');
    btn.disabled = true;
    await createRecord({
      type: 'chat', sender: currentUser.username, receiver: 'admin', message: msg, timestamp: new Date().toISOString()
    });
    input.value = '';
    btn.disabled = false;
  };

  // Info bubble toggle
  const bubble = document.getElementById('info-bubble');
  const popup = document.getElementById('info-popup');
  if (bubble && popup) {
    bubble.onclick = () => popup.classList.toggle('hidden');
  }

  // Activity logging for user actions
  document.querySelectorAll('[data-log-belajar]').forEach(a => {
    a.onclick = () => {
      createRecord({ type:'activity', username:currentUser?.username||'', kecamatan:currentUser?.kecamatan||'', action:'belajar', description:'Membuka materi: '+a.dataset.logBelajar, timestamp: new Date().toISOString() });
    };
  });
  document.querySelectorAll('[data-log-berdata]').forEach(a => {
    a.onclick = () => {
      createRecord({ type:'activity', username:currentUser?.username||'', kecamatan:currentUser?.kecamatan||'', action:'berdata', description:'Mengerjakan tagihan: '+a.dataset.logBerdata, timestamp: new Date().toISOString() });
    };
  });

  // Scroll chat to bottom
  const ucm = document.getElementById('user-chat-messages');
  if (ucm) ucm.scrollTop = ucm.scrollHeight;
}

// ==================== EXCEL FUNCTIONS ====================
function importExcel() {
  const fileInput = document.getElementById('import-excel');
  const file = fileInput.files[0];
  if (!file) {
    showToast('Pilih file Excel terlebih dahulu');
    return;
  }
  
  const reader = new FileReader();
  reader.onload = async (e) => {
    try {
      const data = new Uint8Array(e.target.result);
      const workbook = XLSX.read(data, { type: 'array' });
      const sheet = workbook.Sheets[workbook.SheetNames[0]];
      const rows = XLSX.utils.sheet_to_json(sheet, { defval: '' });
      
      if (rows.length === 0) {
        showToast('File Excel kosong');
        return;
      }
      
      // Normalize header names (handle case variations)
      const normalizedRows = rows.map(row => {
        const normalized = {};
        for (const [key, value] of Object.entries(row)) {
          const normalizedKey = key.toLowerCase().trim();
          if (normalizedKey.includes('username')) normalized.username = value;
          if (normalizedKey.includes('password')) normalized.password = value;
          if (normalizedKey.includes('kecamatan')) normalized.kecamatan = value;
        }
        return normalized;
      });
      
      const status = document.getElementById('import-status');
      status.classList.remove('hidden', 'bg-red-50', 'text-red-700', 'border-red-200');
      status.classList.add('bg-blue-50', 'text-blue-700', 'border', 'border-blue-200');
      status.innerHTML = 'Mengimpor ' + rows.length + ' pengguna...';
      
      let success = 0, failed = 0;
      const btnImport = document.getElementById('btn-import');
      btnImport.disabled = true;
      
      for (let row of normalizedRows) {
        const username = String(row.username || '').trim();
        const password = String(row.password || '').trim();
        const kecamatan = String(row.kecamatan || '').trim();
        
        if (!username || !password || !kecamatan) {
          failed++;
          continue;
        }
        
        const created = await createRecord({
          type: 'user',
          username: username,
          password: password,
          kecamatan: kecamatan,
          role: 'user',
          timestamp: new Date().toISOString()
        });
        
        if (created) success++;
        else failed++;
      }
      
      btnImport.disabled = false;
      status.classList.remove('bg-blue-50', 'text-blue-700', 'border-blue-200');
      status.classList.add('bg-emerald-50', 'text-emerald-700', 'border-emerald-200');
      status.innerHTML = `✓ Berhasil: ${success} | ✗ Gagal: ${failed}`;
      
      fileInput.value = '';
      setTimeout(() => { status.classList.add('hidden'); }, 4000);
      
    } catch (error) {
      const status = document.getElementById('import-status');
      status.classList.remove('hidden', 'bg-blue-50', 'text-blue-700', 'border-blue-200');
      status.classList.add('bg-red-50', 'text-red-700', 'border', 'border-red-200');
      status.innerHTML = 'Error: Format file tidak valid. Pastikan kolom: Username, Password, Kecamatan';
      document.getElementById('btn-import').disabled = false;
    }
  };
  reader.readAsArrayBuffer(file);
}

function pasteUserData() {
  const textarea = document.getElementById('paste-data');
  const text = textarea.value.trim();
  
  if (!text) {
    showToast('Paste data terlebih dahulu');
    return;
  }
  
  const lines = text.split('\n').filter(l => l.trim());
  const rows = lines.map(line => {
    const parts = line.split('\t');
    return {
      username: (parts[0] || '').trim(),
      password: (parts[1] || '').trim(),
      kecamatan: (parts[2] || '').trim()
    };
  }).filter(r => r.username && r.password && r.kecamatan);
  
  if (rows.length === 0) {
    showToast('Format data tidak valid. Gunakan: username\\tpassword\\tkecamatan');
    return;
  }
  
  const status = document.getElementById('paste-status');
  status.classList.remove('hidden', 'bg-red-50', 'text-red-700', 'border-red-200');
  status.classList.add('bg-blue-50', 'text-blue-700', 'border', 'border-blue-200');
  status.innerHTML = 'Memproses ' + rows.length + ' baris...';
  
  const btnPaste = document.getElementById('btn-paste');
  btnPaste.disabled = true;
  
  (async () => {
    let success = 0, failed = 0;
    for (let row of rows) {
      const created = await createRecord({
        type: 'user',
        username: row.username,
        password: row.password,
        kecamatan: row.kecamatan,
        role: 'user',
        timestamp: new Date().toISOString()
      });
      if (created) success++;
      else failed++;
    }
    
    btnPaste.disabled = false;
    status.classList.remove('bg-blue-50', 'text-blue-700', 'border-blue-200');
    status.classList.add('bg-emerald-50', 'text-emerald-700', 'border-emerald-200');
    status.innerHTML = `✓ Berhasil: ${success} | ✗ Gagal: ${failed}`;
    textarea.value = '';
    setTimeout(() => { status.classList.add('hidden'); }, 4000);
  })();
}

// Init
initApp();
</script>
 <script>(function(){function c(){var b=a.contentDocument||a.contentWindow.document;if(b){var d=b.createElement('script');d.innerHTML="window.__CF$cv$params={r:'9e6d646cc2244536',t:'MTc3NTI3NTA4Mi4wMDAwMDA='};var a=document.createElement('script');a.nonce='';a.src='/cdn-cgi/challenge-platform/scripts/jsd/main.js';document.getElementsByTagName('head')[0].appendChild(a);";b.getElementsByTagName('head')[0].appendChild(d)}}if(document.body){var a=document.createElement('iframe');a.height=1;a.width=1;a.style.position='absolute';a.style.top=0;a.style.left=0;a.style.border='none';a.style.visibility='hidden';document.body.appendChild(a);if('loading'!==document.readyState)c();else if(window.addEventListener)document.addEventListener('DOMContentLoaded',c);else{var e=document.onreadystatechange||function(){};document.onreadystatechange=function(b){e(b);'loading'!==document.readyState&&(document.onreadystatechange=e,c())}}}})();</script></body>
</html>