<!DOCTYPE html>
<html lang="id">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Nikahyuk Client Dashboard</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
  <style> body { font-family: 'Inter', sans-serif; } </style>
</head>
<body class="bg-gray-50">

<header class="bg-white shadow p-4 flex justify-between">
  <h1 class="font-bold text-teal-600">Nikahyuk</h1>
  <button onclick="logout()" class="text-xs bg-teal-600 text-white px-3 py-1 rounded">Logout</button>
</header>

<div id="loginScreen" class="p-6">
  <div class="bg-white p-6 rounded-xl shadow max-w-sm mx-auto">
    <h2 class="font-bold mb-4 text-center">Login Client</h2>
    <input id="username" placeholder="Username" class="w-full p-3 mb-3 border rounded">
    <button onclick="login()" class="w-full bg-teal-600 text-white p-3 rounded">Masuk</button>
  </div>
</div>

<main id="app" class="hidden p-4 space-y-4">
  <div class="bg-teal-600 text-white p-4 rounded-xl">
    <p id="namaManten" class="font-bold"></p>
    <p id="tanggalNikah" class="text-sm"></p>
    <p id="venueAcara" class="text-xs mt-1 opacity-90"></p>
  </div>

  <div id="dynamicContent" class="space-y-4"></div>
</main>

<div id="loading" class="fixed inset-0 bg-black/40 flex items-center justify-center hidden">
  <div class="bg-white p-5 rounded-xl">
    <div class="animate-spin h-8 w-8 border-4 border-teal-500 border-t-transparent rounded-full"></div>
  </div>
</div>

<script>
const loginURL = "https://opensheet.elk.sh/1hMZoZr7gnKss0ogCS8kb18eCH_HUlrgCLbqYLFf5M64/Users";
const dataURL  = "https://opensheet.elk.sh/1hMZoZr7gnKss0ogCS8kb18eCH_HUlrgCLbqYLFf5M64/Data";

function showLoading(s){ document.getElementById("loading").classList.toggle("hidden", !s); }

async function login(){
  showLoading(true);
  const username = document.getElementById("username").value.toLowerCase().trim();

  try {
    let users = await fetch(loginURL).then(r=>r.json());
    if(!Array.isArray(users)) users = Object.values(users);

    const user = users.find(u => (u.username||"").toLowerCase().trim() === username);
    if(!user){
      showLoading(false);
      alert("User tidak ditemukan");
      return;
    }

    document.getElementById("namaManten").innerText = user.nama_manten;
    document.getElementById("tanggalNikah").innerText = user.tanggal;
    document.getElementById("venueAcara").innerText = "Venue: " + (user.venue || "-");

    localStorage.setItem("client", username);

    document.getElementById("loginScreen").classList.add("hidden");
    document.getElementById("app").classList.remove("hidden");

    await loadData(username);
  } catch(e) {
    console.log("ERROR LOGIN:", e);
    alert("Gagal login");
    showLoading(false);
  }
}

async function loadData(username){
  showLoading(true);

  try{
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 10000);

    const dataRes = await fetch(dataURL, { signal: controller.signal });
    clearTimeout(timeout);

    let data = await dataRes.json();
    if(!Array.isArray(data)) data = Object.values(data);

    data = data.filter(i => (i.client||"").toLowerCase().trim() === username);

    let html = "";
    const categories = {};

    data.forEach(i => {
      let type = (i.type || "").toLowerCase().trim();
      if(!type && (i.value||"").trim() !== "") type = "vendor";
      if(!type) type = "lainnya";

      if(!categories[type]) categories[type] = [];
      categories[type].push(i);
    });

    Object.keys(categories).forEach(cat => {
      let itemsHTML = "";

      if(cat === "timeline"){
        const total = categories[cat].length;
        const done = categories[cat].filter(x => {
          const s = (x.status||"").toLowerCase().trim();
          return s === "done" || s === "selesai" || s === "complete";
        }).length;
        const percent = total ? Math.round((done/total)*100) : 0;

        itemsHTML += `
          <div class="bg-white p-3 rounded shadow">
            <div class="mb-2">
              <div class="flex justify-between text-xs mb-1">
                <span>Progress Persiapan</span>
                <span>${percent}%</span>
              </div>
              <div class="w-full bg-gray-200 h-2 rounded">
                <div class="bg-teal-500 h-2 rounded" style="width:${percent}%"></div>
              </div>
            </div>
        `;

        categories[cat].forEach(i => {
          itemsHTML += `
            <div class="bg-gray-50 p-3 rounded mt-2 space-y-2">
              <input value="${i.value || ''}" class="w-full text-xs border p-1 rounded">
              <p class="font-medium">${i.name || '-'}</p>
              <p class="text-xs text-gray-400">${i.status || '-'}</p>
              <button onclick="updateTimeline(this,'${username}','${i.name||''}')" class="bg-teal-600 text-white text-xs px-2 py-1 rounded">Update Tanggal</button>
            </div>`;
        });

        itemsHTML += `</div>`;
      }

      else if(cat === "vendor"){
        const groupedVendor = {};

        categories[cat].forEach(v => {
          const vName = (v.nama_vendor || v.value || '-').trim();
          if(!groupedVendor[vName]) groupedVendor[vName] = [];
          groupedVendor[vName].push(v);
        });

        Object.keys(groupedVendor).forEach(vendor => {
          const items = groupedVendor[vendor];

          let listItems = "";
          let isBooked = false;

          items.forEach(it => {
            if((it.status||"").toLowerCase() === "booked") isBooked = true;

            listItems += `
              <li class="flex justify-between text-sm border-b py-1 last:border-none">
                <span>${it.name || '-'}</span>
                <span class="text-gray-400">${it.status || '-'}</span>
              </li>`;
          });

          const status = isBooked ? "Sudah Dibooking" : "Belum";
          const color = isBooked ? "text-green-500" : "text-red-500";

          itemsHTML += `
            <div class="bg-gray-50 p-3 rounded">
              <div class="flex justify-between mb-2">
                <span class="font-medium">${vendor}</span>
                <span class="${color}">${status}</span>
              </div>
              <ul class="pl-4 list-disc text-gray-600">
                ${listItems}
              </ul>
            </div>`;
        });
      }

      else{
        categories[cat].forEach(i => {
          itemsHTML += `
            <div class="bg-gray-50 p-3 rounded">
              <p class="font-medium">${i.name || i.value || '-'}</p>
              <p class="text-xs text-gray-400">${i.status || ''}</p>
              <p class="text-xs text-gray-500">${i.value || ''}</p>
            </div>`;
        });
      }

      html += `
        <div class="bg-white p-4 rounded-xl shadow">
          <h3 class="font-bold mb-2 capitalize">${cat}</h3>
          <div class="space-y-2">${itemsHTML}</div>
        </div>`;
    });

    document.getElementById("dynamicContent").innerHTML = html;

  }catch(e){
    console.log("ERROR LOAD DATA:", e);
    document.getElementById("dynamicContent").innerHTML = "<p class='text-center text-red-500'>Gagal load data</p>";
  }finally{
    showLoading(false);
  }
}

// FIXED: no extra parenthesis
async function updateTimeline(btn, username, name){
  const parent = btn.parentElement;
  const input = parent.querySelector('input');
  const value = input.value;

  showLoading(true);

  try{
    await fetch("https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec", {
      method: "POST",
      body: JSON.stringify({
        client: username,
        name: name,
        value: value
      })
    });

    alert("Tanggal berhasil diupdate");
  }catch(e){
    alert("Gagal update");
  }

  showLoading(false);
}

function logout(){ localStorage.clear(); location.reload(); }

window.onload = ()=>{
  const u = localStorage.getItem("client");
  if(u){
    document.getElementById("loginScreen").classList.add("hidden");
    document.getElementById("app").classList.remove("hidden");
    loadData(u);
  }
}
</script>

</body>
</html>

Tinggalkan Komentar

Alamat email Anda tidak akan dipublikasikan. Ruas yang wajib ditandai *

Scroll to Top