Minggu, 12 April 2026

MPA-Katalog Produk

 





PROMPT

 "Buatlah aplikasi web penjualan produk menggunakan Google Apps Script (GAS) dengan arsitektur MPA.

  1. Database & Storage:

    • Gunakan Google Spreadsheet dengan sheet 'Products' (id, name, price, stock, urlFoto) dan 'Sales' (id, date, total).

    • Gunakan Google Drive untuk menyimpan foto produk.

  2. Backend (Server-side):

    • Code.gs: Implementasikan doGet(e) untuk routing halaman menggunakan parameter URL ?page=.... Buat fungsi include(filename) untuk memuat file HTML.

    • ProductCard.gs: Fungsi getProducts() untuk mengambil data produk dari sheet. Gunakan nilai dari kolom urlFoto langsung sebagai sumber gambar di frontend.

    • ProductForm.gs: Fungsi saveProduct(obj) yang menerima objek metadata dan data gambar (base64). Gunakan DriveApp untuk mengunggah file ke folder tertentu, atur izin file menjadi publik, dapatkan URL gambar langsung (misal menggunakan format https://drive.google.com/uc?export=view&id=ID_FILE), dan simpan URL tersebut ke kolom urlFoto di Spreadsheet.

    • StatsChart.gs: Fungsi getSalesData() untuk dashboard.

  3. Frontend (Client-side):

    • Gunakan Tailwind CSS via CDN.

    • Page Home: Render grid produk menggunakan data dari getProducts().

    • Page Admin: Form upload produk dengan input file. Gunakan FileReader untuk mengubah gambar menjadi base64 sebelum dikirim ke server.

  4. Output: Berikan kode lengkap yang terbagi dalam file .gs dan .html."

HTML

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">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">
            <button 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>
            </button>
          </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">
        <div class="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">
            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>
      </div>
    </section>
    <? } ?>

    <!-- Main Content Area -->
    <main id="katalog" class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
      <? 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>
      <? } ?>
    </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="#" class="hover:text-blue-600 transition">Beranda</a></li>
              <li><a href="#" class="hover:text-blue-600 transition">Katalog</a></li>
              <li><a href="#" 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>
              <li><a href="#" class="hover:text-blue-600 transition">Syarat & Ketentuan</a></li>
            </ul>
          </div>
        </div>
        <div class="border-t border-slate-100 pt-8 flex flex-col md: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">
             <!-- Placeholder Logistik/Pembayaran -->
             <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;
            cartBadge.classList.toggle('hidden', count === 0);
          }
        } catch (e) {
          console.error("Gagal update UI Keranjang:", e);
        }
      }
     
      // Inisialisasi saat window dimuat
      window.addEventListener('load', updateCartUI);
    </script>
  </body>
</html>

Home.html
<div class="mb-8">
  <h1 class="text-3xl font-bold">Katalog Produk</h1>
  <p class="text-gray-500">Temukan produk terbaik untuk kebutuhan Anda.</p>
</div>

<div id="product-grid" class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
  <!-- Produk akan dimuat di sini -->
  <div class="col-span-full text-center py-20">
    <div class="animate-spin inline-block w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full"></div>
    <p class="mt-2 text-gray-400">Memuat produk...</p>
  </div>
</div>

<script>
  function loadProducts() {
    // Memeriksa apakah objek 'google' tersedia (hanya ada di lingkungan Google Apps Script)
    if (typeof google !== 'undefined' && google.script && google.script.run) {
      google.script.run.withSuccessHandler(renderProducts).getProducts();
    } else {
      console.warn("google.script.run tidak terdeteksi. Menggunakan data contoh (Mock Data).");
      // Data contoh untuk keperluan pratinjau di luar Google Apps Script
      const mockProducts = [
        { id: '1', name: 'Produk Contoh A', price: 50000, stock: 10, urlFoto: 'https://placehold.co/600x400?text=Produk+A' },
        { id: '2', name: 'Produk Contoh B', price: 75000, stock: 5, urlFoto: 'https://placehold.co/600x400?text=Produk+B' },
        { id: '3', name: 'Produk Contoh C', price: 120000, stock: 2, urlFoto: 'https://placehold.co/600x400?text=Produk+C' }
      ];
      setTimeout(() => renderProducts(mockProducts), 1000);
    }
  }

  function renderProducts(products) {
    const grid = document.getElementById('product-grid');
    if (!products || !products.length) {
      grid.innerHTML = '<p class="col-span-full text-center text-gray-500">Belum ada produk tersedia.</p>';
      return;
    }

    grid.innerHTML = products.map(p => `
      <div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden hover:shadow-md transition">
        <img src="${p.urlFoto}" alt="${p.name}" class="w-full h-48 object-cover bg-gray-200" onerror="this.src='https://placehold.co/600x400?text=No+Image'">
        <div class="p-4">
          <h3 class="font-bold text-lg mb-1">${p.name}</h3>
          <p class="text-blue-600 font-bold text-xl mb-3">Rp ${Number(p.price).toLocaleString('id-ID')}</p>
          <div class="flex justify-between items-center text-sm text-gray-500 mb-4">
            <span>Stok: ${p.stock}</span>
          </div>
          <button onclick="addToCart('${p.id}', '${p.name}', ${p.price})" class="w-full bg-blue-600 text-white py-2 rounded-lg font-medium hover:bg-blue-700 transition">
            Tambah ke Keranjang
          </button>
        </div>
      </div>
    `).join('');
  }

  function addToCart(id, name, price) {
    let cart = JSON.parse(localStorage.getItem('cart') || '[]');
    const index = cart.findIndex(i => i.id === id);
    if (index > -1) {
      cart[index].qty += 1;
    } else {
      cart.push({ id, name, price, qty: 1 });
    }
    localStorage.setItem('cart', JSON.stringify(cart));
   
    // Pastikan fungsi ini ada di file Index.html
    if (typeof updateCartUI === 'function') {
      updateCartUI();
    }
   
    alert(name + ' ditambahkan ke keranjang!');
  }

  // Jalankan pemuatan produk
  loadProducts();
</script>

Admin.html
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
 
  <!-- Form Upload (Kiri) -->
  <div class="lg:col-span-1 bg-white p-6 rounded-xl shadow-sm border border-gray-100">
    <h2 class="text-xl font-bold mb-6">Tambah Produk Baru</h2>
    <form id="productForm" onsubmit="handleFormSubmit(this); return false;">
      <div class="space-y-4">
        <div>
          <label class="block text-sm font-medium mb-1">Nama Produk</label>
          <input type="text" name="name" required class="w-full p-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none">
        </div>
        <div>
          <label class="block text-sm font-medium mb-1">Harga (Rp)</label>
          <input type="number" name="price" required class="w-full p-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none">
        </div>
        <div>
          <label class="block text-sm font-medium mb-1">Stok</label>
          <input type="number" name="stock" required class="w-full p-2 border rounded-lg focus:ring-2 focus:ring-blue-500 outline-none">
        </div>
        <div>
          <label class="block text-sm font-medium mb-1">Foto Produk</label>
          <input type="file" id="imageInput" accept="image/*" required class="w-full text-sm">
        </div>
        <button type="submit" id="submitBtn" class="w-full bg-gray-800 text-white py-3 rounded-lg font-bold hover:bg-black transition">
          Simpan Produk
        </button>
      </div>
    </form>
    <div id="statusMsg" class="mt-4 text-center hidden"></div>
  </div>

  <!-- Statistik (Kanan) -->
  <div class="lg:col-span-2 space-y-8">
    <div class="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
      <h2 class="text-xl font-bold mb-4 text-center">Tren Penjualan</h2>
      <div class="relative h-[250px] flex items-center justify-center">
        <canvas id="salesChart" height="150"></canvas>
        <div id="chartLoader" class="absolute inset-0 flex items-center justify-center bg-white bg-opacity-80 hidden">
           <p class="text-gray-400 italic">Memuat data grafik...</p>
        </div>
      </div>
    </div>
  </div>
</div>

<script>
  function handleFormSubmit(form) {
    const btn = document.getElementById('submitBtn');
    const msg = document.getElementById('statusMsg');
    const file = document.getElementById('imageInput').files[0];
   
    if (!file) return;
   
    btn.disabled = true;
    btn.innerText = 'Mengunggah...';
    msg.classList.remove('hidden');
    msg.innerHTML = '<span class="text-blue-500 italic">Sedang memproses foto...</span>';

    const reader = new FileReader();
    reader.onload = function(e) {
      const payload = {
        name: form.name.value,
        price: form.price.value,
        stock: form.stock.value,
        image: e.target.result
      };

      if (typeof google !== 'undefined' && google.script && google.script.run) {
        google.script.run.withSuccessHandler(res => {
          btn.disabled = false;
          btn.innerText = 'Simpan Produk';
          if (res.success) {
            msg.innerHTML = '<span class="text-green-600 font-bold">✓ ' + res.message + '</span>';
            form.reset();
          } else {
            msg.innerHTML = '<span class="text-red-600">Error: ' + res.message + '</span>';
          }
        }).saveProduct(payload);
      } else {
        setTimeout(() => {
          btn.disabled = false;
          btn.innerText = 'Simpan Produk';
          msg.innerHTML = '<span class="text-orange-600 font-bold">(!) Mode Pratinjau: Data tidak tersimpan ke Google Sheets.</span>';
        }, 1000);
      }
    };
    reader.readAsDataURL(file);
  }

  function renderChart(data) {
    const loader = document.getElementById('chartLoader');
    if (loader) loader.classList.add('hidden');

    const canvas = document.getElementById('salesChart');
    if (!canvas) return;

    // Proteksi jika Chart.js gagal dimuat dari CDN
    if (typeof Chart === 'undefined') {
      console.error("Gagal memuat Chart.js. Pastikan koneksi internet stabil.");
      const ctx = canvas.getContext('2d');
      ctx.font = "14px sans-serif";
      ctx.textAlign = "center";
      ctx.fillText("Gagal memuat pustaka grafik (Chart.js)", canvas.width / 2, canvas.height / 2);
      return;
    }

    const ctx = canvas.getContext('2d');
    new Chart(ctx, {
      type: 'line',
      data: {
        labels: data.labels || [],
        datasets: [{
          label: 'Total Penjualan (Rp)',
          data: data.values || [],
          borderColor: '#2563eb',
          backgroundColor: 'rgba(37, 99, 235, 0.1)',
          fill: true,
          tension: 0.4
        }]
      },
      options: {
        responsive: true,
        plugins: { legend: { display: false } }
      }
    });
  }

  function initChart() {
    const loader = document.getElementById('chartLoader');
    if (loader) loader.classList.remove('hidden');

    if (typeof google !== 'undefined' && google.script && google.script.run) {
      google.script.run.withSuccessHandler(renderChart).getSalesData();
    } else {
      console.warn("google.script.run tidak terdeteksi. Menggunakan data statistik contoh.");
      const mockStats = {
        labels: ['01/04', '02/04', '03/04', '04/04', '05/04'],
        values: [150000, 230000, 180000, 350000, 290000]
      };
      setTimeout(() => renderChart(mockStats), 500);
    }
  }

  window.addEventListener('load', function() {
    if (typeof updateCartUI === 'function') {
      updateCartUI();
    }
    initChart();
  });
</script>

APPS SCRIPTS

Code.gs
/**
 * Fungsi utama untuk menangani permintaan GET dan mengatur routing MPA.
 */
function doGet(e) {
  const page = e.parameter.page || 'home'; // Default ke halaman home
  let title = 'Toko Online';
 
  if (page === 'admin') title = 'Dashboard Admin - Penjualan';
 
  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);
}

/**
 * Fungsi pembantu untuk memasukkan file HTML ke dalam template utama.
 */
function include(filename) {
  return HtmlService.createHtmlOutputFromFile(filename).getContent();
}

/**
 * Konfigurasi Global
 */
const CONFIG = {
  SPREADSHEET_ID: SpreadsheetApp.getActiveSpreadsheet().getId(),
  FOLDER_FOTO_ID: '1kJJfHH8_QK0vRSmQ1orWYvf7M05bahfu' // Ganti dengan ID folder Drive Anda
};

ProductCard.gs
/**
 * Mengambil semua data produk dari Google Sheet 'Products'.
 */
function getProducts() {
  const ss = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);
  const sheet = ss.getSheetByName('Products');
  const data = sheet.getDataRange().getValues();
 
  // Hapus header dan petakan ke objek
  const headers = data.shift();
  return data.map(row => {
    let obj = {};
    headers.forEach((header, i) => obj[header] = row[i]);
    return obj;
  });
}

ProductForm.gs
/**
 * Menyimpan produk baru, mengunggah foto ke Drive, dan mencatat URL ke Sheet.
 * Menggunakan format googleusercontent untuk memastikan foto tampil dengan stabil.
 */
function saveProduct(obj) {
  try {
    const folder = DriveApp.getFolderById(CONFIG.FOLDER_FOTO_ID);
   
    // Proses upload file (Data Base64 dari frontend)
    const contentType = obj.image.split(';')[0].split(':')[1];
    const bytes = Utilities.base64Decode(obj.image.split(',')[1]);
    const blob = Utilities.newBlob(bytes, contentType, obj.name + '_' + new Date().getTime());
   
    const file = folder.createFile(blob);
   
    // Mengatur izin file agar dapat dilihat oleh siapa saja yang memiliki link
    file.setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
   
    // MENDAPATKAN URL USERCONTENT (Sangat stabil untuk <img> tag)
    // Format: https://lh3.googleusercontent.com/d/[FILE_ID]
    const urlFoto = "https://lh3.googleusercontent.com/d/" + file.getId();
   
    const ss = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);
    const sheet = ss.getSheetByName('Products');
   
    // Simpan ke spreadsheet (id, name, price, stock, urlFoto)
    sheet.appendRow([
      Utilities.getUuid(),
      obj.name,
      obj.price,
      obj.stock,
      urlFoto
    ]);
   
    return { success: true, message: 'Produk berhasil disimpan dengan foto!' };
  } catch (err) {
    return { success: false, message: 'Gagal simpan: ' + err.toString() };
  }
}

StatsChart.gs
/**
 * Mengambil data penjualan untuk kebutuhan grafik Chart.js.
 */
function getSalesData() {
  const ss = SpreadsheetApp.openById(CONFIG.SPREADSHEET_ID);
  const sheet = ss.getSheetByName('Sales');
  if (!sheet) return [];
 
  const data = sheet.getDataRange().getValues();
  data.shift(); // Hapus header
 
  // Agregasi sederhana berdasarkan tanggal
  const stats = {};
  data.forEach(row => {
    const date = Utilities.formatDate(new Date(row[1]), "GMT+7", "dd/MM");
    const total = row[2];
    stats[date] = (stats[date] || 0) + total;
  });
 
  return {
    labels: Object.keys(stats),
    values: Object.values(stats)
  };
}

Dokumentasi Teknis MiniMart Web App

Dokumentasi ini memberikan panduan menyeluruh mengenai struktur, teknologi, dan fungsionalitas aplikasi MiniMart yang dibangun menggunakan Google Apps Script (GAS) dengan arsitektur Multi-Page Application (MPA).

1. Ikhtisar Proyek

MiniMart adalah aplikasi e-commerce ringan yang dirancang untuk UMKM. Aplikasi ini memanfaatkan ekosistem Google (Sheets, Drive, dan Apps Script) sebagai infrastruktur backend dan database, sehingga tidak memerlukan biaya server tambahan.

Teknologi Utama:

  • Backend: Google Apps Script (.gs)

  • Database: Google Sheets

  • Penyimpanan Gambar: Google Drive

  • Frontend Framework: Tailwind CSS (via CDN)

  • Visualisasi Data: Chart.js

  • Tipografi: Plus Jakarta Sans (Google Fonts)

2. Struktur Kode (Index.html)

File Index.html berfungsi sebagai layout utama (Master Template) yang mengatur kerangka aplikasi.

A. Navigasi (Navbar)

  • Menggunakan desain Glassmorphism (transparan dengan efek blur).

  • Sticky Position: Tetap berada di atas saat halaman digulir.

  • Dynamic Active State: Memberikan tanda visual (nav-active) pada menu yang sedang aktif berdasarkan parameter URL.

  • Cart Indicator: Menampilkan jumlah item di keranjang secara real-time yang diambil dari localStorage.

B. Hero Section

Bagian ini hanya muncul di halaman home (?page=home).

  • Visual: Menggunakan elemen dekoratif blob di latar belakang untuk kesan modern.

  • CTA (Call to Action): Tombol utama untuk mengarahkan pengguna langsung ke katalog produk.

C. Konten Utama (Main Content)

Menggunakan logika server-side rendering sederhana dari GAS:

  • <? if (page === 'home') { ?>: Memuat file Home.html.

  • <? else if (page === 'admin') { ?>: Memuat file Admin.html di dalam kontainer kartu putih yang bersih.

3. Arsitektur Data & State

Aplikasi ini menggabungkan data dari server dan penyimpanan lokal:

  • Server-Side Data: Produk dan laporan penjualan ditarik dari Google Sheets melalui fungsi google.script.run.

  • Client-Side State (Shopping Cart): Data keranjang disimpan di localStorage browser. Ini memungkinkan data tetap tersimpan meskipun pengguna berpindah halaman atau menutup browser.

  • URL Routing: Navigasi dilakukan dengan parameter query (misal: exec?page=admin).

4. Panduan Penggunaan Fungsi Script

File ini memiliki fungsi JavaScript utama:

  • updateCartUI(): Berfungsi untuk membaca localStorage, menghitung total kuantitas barang, dan memperbarui angka pada ikon keranjang di navbar.

  • window.addEventListener('load', ...): Memastikan sinkronisasi data terjadi segera setelah halaman selesai dimuat.

5. Instruksi Pemeliharaan

Untuk menjaga performa dan tampilan aplikasi:

  1. Pembaruan Aset: Pastikan link CDN Tailwind dan Chart.js selalu menggunakan versi stabil terbaru.

  2. Keamanan Admin: Saat ini halaman admin dapat diakses melalui URL. Untuk penggunaan produksi, disarankan menambahkan logika autentikasi di Code.gs.

  3. Optimasi Gambar: Gunakan format gambar yang ringan agar proses pemuatan pada bagian katalog tetap cepat.

Dokumentasi ini dihasilkan secara otomatis untuk membantu pengembangan berkelanjutan aplikasi MiniMart.


Panduan Deployment Aplikasi Web Penjualan (GAS)

Ikuti langkah-langkah di bawah ini untuk mengaktifkan aplikasi web Anda menggunakan Google Apps Script, Google Sheets, dan Google Drive.

1. Persiapan Database (Google Sheets)

  1. Buka Google Sheets.

  2. Buat satu Spreadsheet baru dan beri nama (misal: "Database Toko Online").

  3. Buat dua lembar kerja (Sheet) di dalamnya:

    • Sheet 'Products': Isi header pada baris pertama dengan: id, name, price, stock, urlFoto.

    • Sheet 'Sales': Isi header pada baris pertama dengan: id, date, total.

  4. Salin ID Spreadsheet Anda (kode panjang di URL antara /d/ dan /edit).

2. Persiapan Penyimpanan (Google Drive)

  1. Buka Google Drive.

  2. Buat sebuah folder baru khusus untuk menyimpan foto produk.

  3. Klik kanan pada folder tersebut > Dapatkan Tautan (Get Link) > Ubah akses menjadi "Siapa saja yang memiliki link" (Anyone with the link) sebagai "Pelihat" (Viewer).

  4. Salin ID Folder Anda (kode panjang di akhir URL folder saat dibuka).

3. Penyiapan Google Apps Script

  1. Di dalam Google Sheets yang sudah dibuat, klik menu Extensions > Apps Script.

  2. Beri nama proyek Anda (misal: "Web Penjualan Apps").

  3. Buat file-file berikut di dalam editor Apps Script (tekan tanda + di samping tulisan 'Files'):

    • File Script (.gs): Code.gs, ProductCard.gs, ProductForm.gs, StatsChart.gs.

    • File HTML (.html): Index.html, Home.html, Admin.html.

  4. Masukkan kode yang telah kita buat ke masing-masing file tersebut.

  5. Penting: Di dalam Code.gs, pastikan Anda memperbarui bagian CONFIG:

    const CONFIG = {
      SPREADSHEET_ID: 'PASTE_ID_SPREADSHEET_ANDA_DI_SINI',
      FOLDER_FOTO_ID: 'PASTE_ID_FOLDER_DRIVE_ANDA_DI_SINI'
    };
    

4. Proses Deployment (Rilis)

  1. Di pojok kanan atas editor Apps Script, klik tombol biru Deploy > New Deployment.

  2. Pilih jenis deployment dengan menekan ikon gerigi (Select type) > Web App.

  3. Isi konfigurasi sebagai berikut:

    • Description: "Versi 1.0".

    • Execute as: Me (Email Anda).

    • Who has access: Anyone (Ini penting agar pembeli bisa mengakses tanpa login Google).

  4. Klik Deploy.

5. Otorisasi Keamanan

  1. Saat pertama kali deploy, Google akan meminta izin. Klik Authorize Access.

  2. Pilih akun Google Anda.

  3. Jika muncul peringatan "Google hasn't verified this app", klik Advanced > Go to [Nama Proyek Anda] (unsafe).

  4. Klik Allow pada jendela konfirmasi izin akses Drive dan Spreadsheet.

6. Mengakses Aplikasi

  1. Setelah berhasil, Anda akan mendapatkan Web App URL.

  2. Salin URL tersebut. Contoh formatnya: https://script.google.com/macros/s/.../exec.

  3. Buka URL tersebut di browser untuk melihat aplikasi Anda aktif.

  4. Gunakan ?page=admin di akhir URL untuk masuk ke dashboard admin (Contoh: .../exec?page=admin).

Tips Tambahan:

  • Setiap kali Anda mengubah kode, Anda harus melakukan New Deployment atau mengedit deployment lama agar perubahan tersinkronisasi ke URL publik.

  • Gunakan fitur Test Deployments untuk melihat perubahan secara instan sebelum merilisnya ke publik.

Panduan Lengkap Tahapan Pengembangan Aplikasi Web

Dokumen ini menjelaskan langkah-langkah sistematis dalam membangun aplikasi web modern, mulai dari perencanaan hingga peluncuran.

1. Tahap Perencanaan & Analisis (Requirements)

Sebelum menulis kode atau mendesain, Anda harus memahami tujuan aplikasi.

  • Definisi Masalah: Apa masalah yang ingin diselesaikan?

  • Analisis Pengguna: Siapa target penggunanya?

  • Daftar Fitur (Backlog): Menentukan fitur utama (misal: Login, Dashboard, Pembayaran).

  • Pemilihan Stack Teknologi: Memilih bahasa pemrograman dan framework yang sesuai (misal: MERN, Laravel, Django).

2. Tahap Desain (UI/UX)

Fokus pada pengalaman pengguna dan tampilan visual.

  • Wireframing: Membuat kerangka kasar (layout) hitam-putih untuk menentukan penempatan elemen.

  • UI Design: Membuat desain visual tingkat tinggi (High-Fidelity) menggunakan alat seperti Figma atau Adobe XD.

  • Prototyping: Membuat simulasi alur klik antar halaman agar pemangku kepentingan bisa mencoba alur aplikasi sebelum dikembangkan.

3. Pengembangan Front-End (Sisi Klien)

Membangun bagian yang dilihat dan berinteraksi langsung dengan pengguna.

  • Struktur & Gaya: Menggunakan HTML5, CSS3 (atau Tailwind/Sass).

  • Interaktivitas: Menggunakan JavaScript (ES6+).

  • Framework/Library: Menggunakan React.js, Vue.js, atau Angular untuk aplikasi yang kompleks dan reaktif.

  • Responsive Design: Memastikan tampilan aplikasi rapi di perangkat mobile, tablet, dan desktop.

4. Pengembangan Back-End (Sisi Server)

Membangun logika di balik layar, pemrosesan data, dan keamanan.

  • Server Logic: Menggunakan Node.js, Python (Django/Flask), PHP (Laravel), atau Go.

  • Database: * Relational (SQL): PostgreSQL, MySQL (untuk data terstruktur).

    • Non-Relational (NoSQL): MongoDB (untuk data fleksibel).

  • API Development: Membangun RESTful API atau GraphQL sebagai jembatan komunikasi antara Front-End dan Back-End.

  • Autentikasi & Keamanan: Implementasi JWT (JSON Web Tokens), OAuth, dan enkripsi data.

5. Pengujian (Testing & QA)

Menjamin aplikasi berjalan tanpa kendala (bug).

  • Unit Testing: Menguji fungsi-fungsi kecil secara individu.

  • Integration Testing: Memastikan Front-End dan Back-End terhubung dengan benar.

  • User Acceptance Testing (UAT): Pengujian akhir oleh pengguna untuk memastikan aplikasi memenuhi ekspektasi.

6. Deployment & Pemeliharaan (DevOps)

Membawa aplikasi dari komputer lokal ke server publik.

  • Version Control: Menggunakan Git (GitHub/GitLab) untuk mengelola perubahan kode.

  • Hosting: * Static/Frontend: Vercel, Netlify.

    • Fullstack/Cloud: AWS, Google Cloud, DigitalOcean.

  • CI/CD Pipeline: Otomatisasi proses pengujian dan pengunggahan kode.

  • Monitoring: Memantau performa dan error setelah aplikasi rilis.

Dokumen ini dapat digunakan sebagai checklist proyek pengembangan web Anda.


Spesifikasi Aplikasi Penjualan Produk (E-Commerce)

Dokumen ini berisi detail teknis pengembangan aplikasi penjualan berdasarkan tahapan standar industri.

1. Analisis & Perencanaan (Requirements)

  • Tujuan: Platform bagi pengguna untuk membeli barang dan admin untuk mengelola stok.

  • User Roles:

    • Pembeli: Mencari barang, menambah ke keranjang, checkout.

    • Admin: Mengunggah produk, melihat statistik penjualan di dashboard.

  • Fitur Utama:

    1. Katalog Produk (Display).

    2. Sistem Keranjang (Shopping Cart).

    3. Manajemen Produk (Upload/Edit/Delete).

    4. Dashboard Statistik (Ringkasan Penjualan).

    5. Integrasi Google Drive: Penyimpanan foto produk secara otomatis ke folder Drive dan penyimpanan URL-nya di database.

2. Tahap Desain (UI/UX)

  • Wireframe:

    • Beranda: Grid kartu produk dengan fitur pencarian.

    • Halaman Produk: Gambar besar, deskripsi, dan tombol "Tambah ke Keranjang".

    • Admin Panel: Sidebar navigasi menuju "Data Produk" dan "Laporan Penjualan".

  • Desain Visual: Tema bersih (clean) dengan warna primer biru/hijau untuk membangun kepercayaan.

  • Tools: Figma.

3. Pengembangan Front-End & Back-End (GAS Architecture)

Untuk implementasi menggunakan Google Apps Script (GAS), arsitektur menggunakan Multi-Page Application (MPA) yang dirender dari server.

  • Teknologi:

    • Backend: Google Apps Script (GAS) & Google Sheets (sebagai Database).

    • Storage: Google Drive API (untuk menyimpan file gambar produk dan menghasilkan URL publik).

    • Frontend: HTML5, Tailwind CSS (via CDN), Chart.js untuk visualisasi.

  • Struktur File GAS:

    • Code.gs: Logika utama doGet(e) untuk routing halaman dan fungsi database global.

    • ProductCard.gs: Fungsi backend untuk mengambil data produk dari sheet termasuk kolom urlFoto.

    • ProductForm.gs: Fungsi backend untuk menerima input form, mengunggah ke Google Drive, mengambil URL tautan langsung, dan menyimpan URL tersebut ke Spreadsheet.

    • StatsChart.gs: Fungsi backend untuk agregat data penjualan guna kebutuhan grafik.

4. Prompt Implementasi (Untuk AI Code Generator)

Gunakan prompt di bawah ini untuk menghasilkan kode aplikasi berdasarkan spesifikasi di atas:

Prompt: > "Buatlah aplikasi web penjualan produk menggunakan Google Apps Script (GAS) dengan arsitektur MPA.

  1. Database & Storage:

    • Gunakan Google Spreadsheet dengan sheet 'Products' (id, name, price, stock, urlFoto) dan 'Sales' (id, date, total).

    • Gunakan Google Drive untuk menyimpan foto produk.

  2. Backend (Server-side):

    • Code.gs: Implementasikan doGet(e) untuk routing halaman menggunakan parameter URL ?page=.... Buat fungsi include(filename) untuk memuat file HTML.

    • ProductCard.gs: Fungsi getProducts() untuk mengambil data produk dari sheet. Gunakan nilai dari kolom urlFoto langsung sebagai sumber gambar di frontend.

    • ProductForm.gs: Fungsi saveProduct(obj) yang menerima objek metadata dan data gambar (base64). Gunakan DriveApp untuk mengunggah file ke folder tertentu, atur izin file menjadi publik, dapatkan URL gambar langsung (misal menggunakan format https://drive.google.com/uc?export=view&id=ID_FILE), dan simpan URL tersebut ke kolom urlFoto di Spreadsheet.

    • StatsChart.gs: Fungsi getSalesData() untuk dashboard.

  3. Frontend (Client-side):

    • Gunakan Tailwind CSS via CDN.

    • Page Home: Render grid produk menggunakan data dari getProducts().

    • Page Admin: Form upload produk dengan input file. Gunakan FileReader untuk mengubah gambar menjadi base64 sebelum dikirim ke server.

  4. Output: Berikan kode lengkap yang terbagi dalam file .gs dan .html."

5. Pengujian (Testing)

  • Scenario Upload: Memastikan file gambar masuk ke Google Drive dan kolom urlFoto di Spreadsheet terisi dengan link yang valid.

  • Scenario Display: Memastikan tag <img> di halaman pembeli menggunakan atribut src dari urlFoto dan gambar muncul dengan benar.

  • Scenario Keranjang: Memastikan data produk (termasuk foto) tersimpan di localStorage dengan benar saat ditambahkan ke keranjang.

6. Deployment (DevOps)

  1. Buka script.google.com.

  2. Buat folder di Google Drive dan catat Folder ID-nya untuk digunakan di ProductForm.gs.

  3. Salin kode hasil prompt ke masing-masing file.

  4. Klik Deploy > New Deployment sebagai Web App.

  5. Set akses ke Anyone.

  6. Berikan izin akses (Authorization) yang diperlukan untuk Google Drive dan Spreadsheet.

Gunakan dokumen ini sebagai panduan teknis saat memulai coding.

Tidak ada komentar:

Posting Komentar

MPA - Ruang Karir

  Ruang Karir Double Track Ruang Karir Double Track adalah platform job market berbasis web yang dirancang khusus untuk menghubungkan alum...