APS

import React, { useState, useEffect } from 'react'; import { ShoppingCart, Plus, Box, Trash2, Search, Receipt, Store, CheckCircle, X, QrCode, Banknote, Printer, Loader2 } from 'lucide-react'; // --- PASTE URL WEB APP ANDA DI SINI --- // Pastikan URL berakhiran /exec const SCRIPT_URL = 'https://script.google.com/macros/s/AKfycbwNShER635nllYQxWygb8tBjWtaZjHH7xXdEt3ezLHacHWGPRVSF41GfZDbNV9BZ0dHMQ/exec'; export default function App() { const [activeTab, setActiveTab] = useState('kasir'); const [products, setProducts] = useState([]); // Kosongkan awalnya, akan diisi dari Sheets const [cart, setCart] = useState([]); const [searchQuery, setSearchQuery] = useState(''); const [notification, setNotification] = useState(null); const [isLoading, setIsLoading] = useState(false); // State untuk efek Loading // Ambil data dari Google Sheets saat aplikasi pertama kali dibuka useEffect(() => { if (SCRIPT_URL !== 'PASTE_URL_WEB_APP_ANDA_DISINI') { fetchCatalog(); } }, []); const fetchCatalog = async () => { setIsLoading(true); try { const response = await fetch(SCRIPT_URL); const result = await response.json(); if (result.status === 'success') { setProducts(result.data); } } catch (error) { console.error('Fetch error:', error); showNotification('Gagal mengambil data dari server', 'error'); } setIsLoading(false); }; // State untuk Pembayaran const [showPaymentModal, setShowPaymentModal] = useState(false); const [paymentMethod, setPaymentMethod] = useState('cash'); // 'cash' atau 'qris' const [cashAmount, setCashAmount] = useState(''); const [logoError, setLogoError] = useState(false); // State untuk Nota/Receipt const [showReceiptModal, setShowReceiptModal] = useState(false); const [receiptData, setReceiptData] = useState(null); // State untuk Token Listrik const [tokenListrik, setTokenListrik] = useState(''); // State untuk form tambah katalog const [newItem, setNewItem] = useState({ name: '', price: '', stock: '' }); // --- Fungsi Format --- const formatRupiah = (number) => { return new Intl.NumberFormat('id-ID', { style: 'currency', currency: 'IDR', minimumFractionDigits: 0 }).format(number); }; const handleTokenChange = (e) => { const rawValue = e.target.value.replace(/\D/g, ''); const truncated = rawValue.slice(0, 20); const formatted = truncated.match(/.{1,4}/g)?.join('-') || truncated; setTokenListrik(formatted); }; // --- Fungsi Notifikasi --- const showNotification = (message, type = 'success') => { setNotification({ message, type }); setTimeout(() => setNotification(null), 3000); }; // --- Fungsi Kasir --- const addToCart = (product) => { if (product.stock <= 0) { showNotification('Stok barang habis!', 'error'); return; } setCart(prevCart => { const existingItem = prevCart.find(item => item.id === product.id); if (existingItem) { if (existingItem.qty >= product.stock) { showNotification('Maksimal stok tercapai!', 'error'); return prevCart; } return prevCart.map(item => item.id === product.id ? { ...item, qty: item.qty + 1 } : item ); } return [...prevCart, { ...product, qty: 1 }]; }); }; const removeFromCart = (id) => { setCart(prevCart => prevCart.filter(item => item.id !== id)); }; const updateCartQty = (id, newQty) => { if (newQty < 1) return; const product = products.find(p => p.id === id); if (product && newQty > product.stock) { showNotification('Melebihi stok yang ada!', 'error'); return; } setCart(prevCart => prevCart.map(item => item.id === id ? { ...item, qty: newQty } : item) ); }; const cartTotal = cart.reduce((sum, item) => sum + (item.price * item.qty), 0); const handleCheckoutClick = () => { if (cart.length === 0) { showNotification('Keranjang masih kosong!', 'error'); return; } setShowPaymentModal(true); setPaymentMethod('cash'); setCashAmount(''); setTokenListrik(''); }; const processPayment = async () => { let amount = 0; let change = 0; if (paymentMethod === 'cash') { amount = parseInt(cashAmount) || 0; if (amount < cartTotal) { showNotification('Uang tunai kurang dari total belanja!', 'error'); return; } change = amount - cartTotal; } const transactionData = { date: new Date().toLocaleString('id-ID'), items: [...cart], total: cartTotal, paymentMethod, cashAmount: amount, change, tokenListrik }; setIsLoading(true); try { // Menggunakan mode no-cors atau memastikan headers kompatibel dengan Apps Script await fetch(SCRIPT_URL, { method: 'POST', mode: 'no-cors', // Menghindari isu preflight CORS di beberapa browser headers: { 'Content-Type': 'text/plain' }, body: JSON.stringify({ action: 'checkout', transaction: transactionData }) }); // Optimistic update karena no-cors tidak mengembalikan response body setProducts(prevProducts => { let updatedProducts = [...prevProducts]; cart.forEach(cartItem => { const index = updatedProducts.findIndex(p => p.id === cartItem.id); if (index !== -1) { updatedProducts[index].stock -= cartItem.qty; } }); return updatedProducts; }); setReceiptData(transactionData); setCart([]); setShowPaymentModal(false); setShowReceiptModal(true); showNotification(`Transaksi berhasil diproses!`, 'success'); } catch (error) { console.error('Checkout error:', error); showNotification('Gagal mengirim transaksi ke server', 'error'); } setIsLoading(false); }; // --- Fungsi Katalog --- const handleAddProduct = async (e) => { e.preventDefault(); if (!newItem.name || !newItem.price || !newItem.stock) { showNotification('Harap isi semua kolom!', 'error'); return; } const newProduct = { id: Date.now(), name: newItem.name, price: parseInt(newItem.price), stock: parseInt(newItem.stock) }; setIsLoading(true); try { await fetch(SCRIPT_URL, { method: 'POST', mode: 'no-cors', headers: { 'Content-Type': 'text/plain' }, body: JSON.stringify({ action: 'add_product', product: newProduct }) }); setProducts([...products, newProduct]); setNewItem({ name: '', price: '', stock: '' }); showNotification('Barang berhasil ditambahkan!', 'success'); } catch (error) { showNotification('Gagal menyimpan barang ke server', 'error'); } setIsLoading(false); }; const handleDeleteProduct = async (id) => { setIsLoading(true); try { await fetch(SCRIPT_URL, { method: 'POST', mode: 'no-cors', headers: { 'Content-Type': 'text/plain' }, body: JSON.stringify({ action: 'delete_product', id: id }) }); setProducts(products.filter(p => p.id !== id)); setCart(cart.filter(item => item.id !== id)); showNotification('Barang dihapus.', 'success'); } catch (error) { showNotification('Gagal menghapus barang dari server', 'error'); } setIsLoading(false); }; const filteredProducts = products.filter(product => product.name.toLowerCase().includes(searchQuery.toLowerCase()) ); return (
{/* Header */}
{!logoError ? ( Logo Toko Adela PS setLogoError(true)} /> ) : ( )} Toko Adela PS
{/* Notification Toast */} {notification && (
{notification.type === 'success' ? : } {notification.message}
)} {/* Loading Overlay */} {isLoading && (

Memproses sinkronisasi data...

)} {/* Main Content */}
{/* --- VIEW: KASIR (POS) --- */} {activeTab === 'kasir' && (
{/* Daftar Produk */}
setSearchQuery(e.target.value)} />
{filteredProducts.map(product => (
addToCart(product)} className="bg-white rounded-xl shadow-sm border border-slate-200 p-4 cursor-pointer hover:shadow-md hover:border-blue-300 transition-all active:scale-95 flex flex-col h-full" >

{product.name}

{formatRupiah(product.price)}

Stok: {product.stock} {product.stock > 0 ? ( ) : ( Habis )}
))} {filteredProducts.length === 0 && (
Tidak ada barang yang cocok dengan pencarian.
)}
{/* Keranjang (Cart) */}

Keranjang

{cart.reduce((sum, item) => sum + item.qty, 0)} Item
{cart.length === 0 ? (

Keranjang kosong

) : ( cart.map(item => (

{item.name}

{formatRupiah(item.price)}

{item.qty}
)) )}
Total Harga {formatRupiah(cartTotal)}
)} {/* --- MODAL PEMBAYARAN --- */} {showPaymentModal && (

Pilih Metode Pembayaran

Total Tagihan

{formatRupiah(cartTotal)}

{paymentMethod === 'cash' ? (
setCashAmount(e.target.value)} />
{cashAmount && parseInt(cashAmount) >= cartTotal && (
Kembalian: {formatRupiah(parseInt(cashAmount) - cartTotal)}
)}
) : (

Scan QR Code ini menggunakan aplikasi M-Banking atau E-Wallet pelanggan.

)}

Isi hanya jika pelanggan membeli token listrik.

)} {/* --- MODAL NOTA (RECEIPT) --- */} {showReceiptModal && receiptData && (
{/* Area Struk yang akan dicetak */}

Toko Adela PS

Waktu Belanja: {receiptData.date}

{receiptData.items.map(item => (

{item.name}

{item.qty} x {formatRupiah(item.price)}

{formatRupiah(item.price * item.qty)}
))}
Total Tagihan {formatRupiah(receiptData.total)}
Metode Pembayaran {receiptData.paymentMethod}
{receiptData.paymentMethod === 'cash' && ( <>
Tunai {formatRupiah(receiptData.cashAmount)}
Kembalian {formatRupiah(receiptData.change)}
)}
{/* Bagian Token Listrik pada Nota */} {receiptData.tokenListrik && (

TOKEN LISTRIK

{receiptData.tokenListrik}

)}

Terima kasih sudah belanja 🙏

Ditunggu kedatangannya lagi ya!

{/* Tombol Aksi (Disembunyikan saat dicetak) */}
)} {/* --- VIEW: KATALOG --- */} {activeTab === 'katalog' && (

Tambah Barang Baru

setNewItem({...newItem, name: e.target.value})} />
setNewItem({...newItem, price: e.target.value})} />
setNewItem({...newItem, stock: e.target.value})} />

Daftar Katalog Barang

{products.map(product => ( ))} {products.length === 0 && ( )}
Nama Barang Harga Stok Aksi
{product.name} {formatRupiah(product.price)} 10 ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}> {product.stock}
Belum ada barang di katalog. Silakan tambah barang baru.
)}
); }

Komentar

Postingan populer dari blog ini