Minggu, 12 April 2026

MPA - Sales dan Payment

 







Sales.gs

/**
 * Memproses checkout: Simpan ke sheet Sales dan kurangi stok di sheet Products.
 */
function processCheckout(orderData) {
  try {
    const ss = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);
    const salesSheet = ss.getSheetByName('Sales');
    const productSheet = ss.getSheetByName('Products');
   
    // 1. Simpan Data Penjualan
    // Kolom: id, date, customer_name, total_amount, items_json
    salesSheet.appendRow([
      Utilities.getUuid(),
      new Date(),
      orderData.customerName,
      orderData.totalAmount,
      JSON.stringify(orderData.items)
    ]);
   
    // 2. Update Stok Produk (Sederhana)
    const productData = productSheet.getDataRange().getValues();
    orderData.items.forEach(item => {
      for (let i = 1; i < productData.length; i++) {
        if (productData[i][0] == item.id) { // Cocokkan ID
          const currentStock = productData[i][3];
          productSheet.getRange(i + 1, 4).setValue(currentStock - item.qty);
          break;
        }
      }
    });
   
    return { success: true, message: 'Pembayaran berhasil diproses!' };
  } catch (err) {
    return { success: false, message: err.toString() };
  }
}

Checkout.html

<div class="max-w-4xl mx-auto">
  <div class="mb-8">
    <h1 class="text-3xl font-bold">Checkout</h1>
    <p class="text-slate-500">Selesaikan pesanan Anda di bawah ini.</p>
  </div>

  <div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
    <!-- Ringkasan Pesanan -->
    <div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-200">
      <h2 class="text-xl font-bold mb-4">Ringkasan Pesanan</h2>
      <div id="checkout-items" class="space-y-4 mb-6">
        <!-- Item keranjang akan dimuat di sini -->
      </div>
      <div class="border-t pt-4 flex justify-between items-center font-bold text-lg text-blue-600">
        <span>Total Bayar:</span>
        <span id="checkout-total">Rp 0</span>
      </div>
    </div>

    <!-- Form Pembayaran -->
    <div class="bg-white p-6 rounded-2xl shadow-sm border border-slate-200">
      <h2 class="text-xl font-bold mb-4">Data Pengiriman</h2>
      <form id="paymentForm" onsubmit="handlePayment(this); return false;">
        <div class="space-y-4">
          <div>
            <label class="block text-sm font-medium mb-1">Nama Lengkap</label>
            <input type="text" name="customerName" required class="w-full p-3 border rounded-xl focus:ring-2 focus:ring-blue-500 outline-none">
          </div>
          <div>
            <label class="block text-sm font-medium mb-1">Alamat Pengiriman</label>
            <textarea name="address" required class="w-full p-3 border rounded-xl focus:ring-2 focus:ring-blue-500 outline-none h-24"></textarea>
          </div>
          <div>
            <label class="block text-sm font-medium mb-1">Metode Pembayaran</label>
            <select name="method" class="w-full p-3 border rounded-xl focus:ring-2 focus:ring-blue-500 outline-none">
              <option value="Transfer Bank">Transfer Bank</option>
              <option value="E-Wallet">E-Wallet (Gopay/OVO)</option>
              <option value="COD">Bayar di Tempat (COD)</option>
            </select>
          </div>
          <button type="submit" id="payBtn" class="w-full bg-blue-600 text-white py-4 rounded-xl font-bold hover:bg-blue-700 transition transform hover:-translate-y-1">
            Bayar Sekarang
          </button>
        </div>
      </form>
      <div id="payStatus" class="mt-4 text-center hidden"></div>
    </div>
  </div>
</div>

<script>
  let cartData = [];
  let totalAmount = 0;

  function loadCheckout() {
    cartData = JSON.parse(localStorage.getItem('cart') || '[]');
    const container = document.getElementById('checkout-items');
   
    if (cartData.length === 0) {
      container.innerHTML = '<p class="text-slate-400">Keranjang Anda kosong.</p>';
      document.getElementById('payBtn').disabled = true;
      document.getElementById('payBtn').classList.add('opacity-50');
      return;
    }

    totalAmount = cartData.reduce((acc, item) => acc + (item.price * item.qty), 0);
    document.getElementById('checkout-total').innerText = 'Rp ' + totalAmount.toLocaleString('id-ID');

    container.innerHTML = cartData.map(item => `
      <div class="flex justify-between items-center text-sm border-b border-slate-50 pb-2">
        <div>
          <p class="font-semibold">${item.name}</p>
          <p class="text-slate-400">${item.qty} x Rp ${item.price.toLocaleString('id-ID')}</p>
        </div>
        <p class="font-bold">Rp ${(item.price * item.qty).toLocaleString('id-ID')}</p>
      </div>
    `).join('');
  }

  function handlePayment(form) {
    const btn = document.getElementById('payBtn');
    const msg = document.getElementById('payStatus');
   
    btn.disabled = true;
    btn.innerText = 'Memproses...';
    msg.classList.remove('hidden');
    msg.innerHTML = '<p class="text-blue-500 animate-pulse">Menghubungkan ke server...</p>';

    const orderData = {
      customerName: form.customerName.value,
      totalAmount: totalAmount,
      items: cartData
    };

    if (typeof google !== 'undefined' && google.script && google.script.run) {
      google.script.run.withSuccessHandler(res => {
        if (res.success) {
          localStorage.removeItem('cart');
          updateCartUI();
          msg.innerHTML = '<div class="p-4 bg-green-50 text-green-700 rounded-xl font-bold">✓ Pesanan Berhasil! Terima kasih telah berbelanja.</div>';
          form.reset();
          setTimeout(() => {
            window.location.href = '<?= ScriptApp.getService().getUrl() ?>?page=home';
          }, 3000);
        } else {
          btn.disabled = false;
          btn.innerText = 'Bayar Sekarang';
          msg.innerHTML = '<p class="text-red-600">Terjadi kesalahan: ' + res.message + '</p>';
        }
      }).processCheckout(orderData);
    } else {
      // Mock for Preview
      setTimeout(() => {
        msg.innerHTML = '<p class="text-orange-600 font-bold">(!) Mode Pratinjau: Pembayaran disimulasikan sukses.</p>';
        localStorage.removeItem('cart');
        updateCartUI();
      }, 1500);
    }
  }

  loadCheckout();
</script>

Update Code.gs

/**
 * Konfigurasi Global Aplikasi
 * Diletakkan di atas agar mudah diakses oleh fungsi manapun.
 */
const CONFIG = {
  // Gunakan try-catch atau pastikan skrip terikat dengan Sheet
  SPREADSHEET_ID: SpreadsheetApp.getActiveSpreadsheet() ? SpreadsheetApp.getActiveSpreadsheet().getId() : "ID_MANUAL_JIKA_DIPERLUKAN",
  FOLDER_FOTO_ID: '1kJJfHH8_QK0vRSmQ1orWYvf7M05bahfu'
};

function doGet(e) {
  // Pastikan e.parameter ada untuk menghindari error saat testing tanpa URL param
  const params = e ? e.parameter : {};
  const page = params.page || 'home';
 
  let title = 'Toko Online - MiniMart';
 
  // Mapping Judul (Lebih bersih jika halaman bertambah banyak)
  const titles = {
    'admin': 'Dashboard Admin - MiniMart',
    'checkout': 'Checkout & Pembayaran - MiniMart',
    'home': 'Toko Online - MiniMart'
  };
 
  if (titles[page]) {
    title = titles[page];
  }

  // Pastikan file 'Index' ada di daftar file proyek Anda
  const template = HtmlService.createTemplateFromFile('Index');
  template.page = page;
 
  return template.evaluate()
    .setTitle(title)
    .addMetaTag('viewport', 'width=device-width, initial-scale=1')
    .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

function include(filename) {
  // Tambahkan try-catch agar jika file HTML tidak ada, aplikasi tidak langsung mati
  try {
    return HtmlService.createHtmlOutputFromFile(filename).getContent();
  } catch (err) {
    return ``;
  }
}

Update Index.html

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <!-- Tailwind CSS CDN -->
    <script src="https://cdn.tailwindcss.com"></script>
    <!-- Chart.js -->
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
      @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap');
      body { font-family: 'Plus Jakarta Sans', sans-serif; }
      .nav-active { @apply text-blue-600 font-semibold; }
      .glass-nav {
        background: rgba(255, 255, 255, 0.8);
        backdrop-filter: blur(12px);
        -webkit-backdrop-filter: blur(12px);
      }
    </style>
  </head>
  <body class="bg-slate-50 text-slate-900">
   
    <!-- Navbar -->
    <nav class="glass-nav border-b border-slate-200 sticky top-0 z-50">
      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div class="flex justify-between h-16 items-center">
          <div class="flex items-center gap-8">
            <a href="<?= ScriptApp.getService().getUrl() ?>?page=home" class="flex items-center gap-2">
              <div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
                <svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"></path></svg>
              </div>
              <span class="text-xl font-bold tracking-tight text-slate-900 text-[18px]">MiniMart</span>
            </a>
            <div class="hidden md:flex space-x-8">
              <a href="<?= ScriptApp.getService().getUrl() ?>?page=home" class="text-sm transition hover:text-blue-600 <?= page === 'home' ? 'nav-active' : 'text-slate-600' ?>">Beranda</a>
              <a href="<?= ScriptApp.getService().getUrl() ?>?page=admin" class="text-sm transition hover:text-blue-600 <?= page === 'admin' ? 'nav-active' : 'text-slate-600' ?>">Dashboard Admin</a>
            </div>
          </div>
          <div class="flex items-center gap-4">
            <!-- Ikon Keranjang yang mengarah ke halaman Checkout -->
            <a href="<?= ScriptApp.getService().getUrl() ?>?page=checkout" class="p-2 text-slate-600 hover:bg-slate-100 rounded-full transition relative">
              <span id="cart-count" class="absolute top-0 right-0 bg-blue-600 text-white text-[10px] font-bold rounded-full h-4 w-4 flex items-center justify-center border-2 border-white">0</span>
              <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"></path></svg>
            </a>
          </div>
        </div>
      </div>
    </nav>

    <!-- Hero Section (Hanya tampil di Home) -->
    <? if (page === 'home') { ?>
    <section class="relative overflow-hidden bg-white pt-16 pb-20 lg:pt-24 lg:pb-32">
      <div class="absolute top-0 left-1/2 -translate-x-1/2 w-full h-full pointer-events-none overflow-hidden">
        <div class="absolute -top-[10%] -left-[10%] w-[40%] h-[40%] bg-blue-50 rounded-full blur-3xl opacity-50"></div>
        <div class="absolute bottom-0 right-0 w-[30%] h-[30%] bg-indigo-50 rounded-full blur-3xl opacity-50"></div>
      </div>
     
      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 relative text-center">
        <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-50 text-blue-700 mb-6">
          🎉 Promo Ramadhan: Diskon hingga 50%
        </span>
        <h1 class="text-4xl md:text-6xl font-extrabold tracking-tight text-slate-900 mb-6 leading-tight">
          Belanja Kebutuhan Harian <br/>
          <span class="text-blue-600">Lebih Cepat & Mudah</span>
        </h1>
        <p class="text-lg text-slate-600 max-w-2xl mx-auto mb-10">
          Dapatkan produk berkualitas tinggi langsung dari genggaman Anda. Pengiriman cepat, transaksi aman, dan harga yang selalu bersaing setiap harinya.
        </p>
        <div class="flex flex-col sm:flex-row justify-center gap-4">
          <a href="#katalog" class="px-8 py-4 bg-blue-600 text-white rounded-xl font-bold shadow-lg shadow-blue-200 hover:bg-blue-700 transition transform hover:-translate-y-1">
            Mulai Belanja
          </a>
          <a href="<?= ScriptApp.getService().getUrl() ?>?page=admin" class="px-8 py-4 bg-white text-slate-700 border border-slate-200 rounded-xl font-bold hover:bg-slate-50 transition">
            Kelola Toko
          </a>
        </div>
      </div>
    </section>
    <? } ?>

    <!-- Main Content Area -->
    <main id="katalog" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12 min-h-[60vh]">
      <? if (page === 'home') { ?>
        <?!= include('Home'); ?>
      <? } else if (page === 'admin') { ?>
        <div class="bg-white rounded-2xl p-8 shadow-sm border border-slate-200">
           <?!= include('Admin'); ?>
        </div>
      <? } else if (page === 'checkout') { ?>
        <?!= include('Checkout'); ?>
      <? } ?>
    </main>

    <!-- Footer -->
    <footer class="bg-white border-t border-slate-200 pt-16 pb-8">
      <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        <div class="grid grid-cols-1 md:grid-cols-4 gap-12 mb-12">
          <div class="col-span-1 md:col-span-2">
            <div class="flex items-center gap-2 mb-4">
              <div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center text-white font-bold">M</div>
              <span class="text-xl font-bold">MiniMart</span>
            </div>
            <p class="text-slate-500 max-w-sm">
              Solusi e-commerce modern menggunakan Google Apps Script. Membantu UMKM untuk go-digital dengan mudah dan efisien.
            </p>
          </div>
          <div>
            <h4 class="font-bold mb-4">Navigasi</h4>
            <ul class="space-y-2 text-slate-600 text-sm">
              <li><a href="<?= ScriptApp.getService().getUrl() ?>?page=home" class="hover:text-blue-600 transition">Beranda</a></li>
              <li><a href="#katalog" class="hover:text-blue-600 transition">Katalog</a></li>
              <li><a href="<?= ScriptApp.getService().getUrl() ?>?page=admin" class="hover:text-blue-600 transition">Dashboard Admin</a></li>
            </ul>
          </div>
          <div>
            <h4 class="font-bold mb-4">Bantuan</h4>
            <ul class="space-y-2 text-slate-600 text-sm">
              <li><a href="#" class="hover:text-blue-600 transition">FAQ</a></li>
              <li><a href="#" class="hover:text-blue-600 transition">Hubungi Kami</a></li>
            </ul>
          </div>
        </div>
        <div class="border-t border-slate-100 pt-8 flex flex-col md:flex-row justify-between items-center gap-4">
          <p class="text-slate-400 text-sm">© 2024 MiniMart Ecosystem. All rights reserved.</p>
          <div class="flex gap-6 grayscale opacity-50">
             <span class="text-xs font-bold italic">VISA</span>
             <span class="text-xs font-bold italic">MASTERCARD</span>
             <span class="text-xs font-bold italic">GOPAY</span>
          </div>
        </div>
      </div>
    </footer>

    <script>
      /**
       * Memperbarui antarmuka jumlah keranjang dari localStorage.
       */
      function updateCartUI() {
        try {
          const cart = JSON.parse(localStorage.getItem('cart') || '[]');
          const count = cart.reduce((acc, item) => acc + (item.qty || 0), 0);
          const cartBadge = document.getElementById('cart-count');
          if (cartBadge) {
            cartBadge.innerText = count;
            // Badge tetap terlihat agar pengguna tahu ada akses ke checkout
          }
        } catch (e) {
          console.error("Gagal update UI Keranjang:", e);
        }
      }
     
      window.addEventListener('load', updateCartUI);
    </script>
  </body>
</html>



Tidak ada komentar:

Posting Komentar

MPA - Sales dan Payment

  https://script.google.com/macros/s/AKfycbzZiOuWv9vMWKHS1VcQyhSZ2heKjAAzsVV80cDtAD1U78pc8JIVVjyinE2y0FHfpODq/exec?page=checkout https://scr...