Prompt manual
Buatkan aplikasi Inventaris barang kelauar masuk dengan ketentuan: Sistem Akses: Login Password Custom Menu: Dashboard, Daftar Barang, Barang Masuk, Barang Keluar Detail Data: Kode Barang, Nama Barang, Kategori, Stok Awal, Satuan, Lokasi Rak data tersimpan di google spreadsheet dan publish di Google Apps Script. buatkan Code.gs (termasuk setupdatabase untuk membuat spreadsheet) Index.html Petunjuk PenggunaanCode.gs
/**
* APLIKASI INVENTARIS BARANG (GAS + SPREADSHEET)
* Fitur: Login, Dashboard, Master Barang, Barang Masuk, Barang Keluar
*/
const SPREADSHEET_ID = SpreadsheetApp.getActiveSpreadsheet().getId();
const APP_PASSWORD = "admin123"; // Password default untuk login
function doGet() {
return HtmlService.createTemplateFromFile('Index')
.evaluate()
.setTitle('Sistem Inventaris Barang')
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}
// Fungsi Setup Database (Jalankan Sekali Saja)
function setupDatabase() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 1. Sheet Master Barang
let sheetMaster = ss.getSheetByName("MasterBarang");
if (!sheetMaster) {
sheetMaster = ss.insertSheet("MasterBarang");
sheetMaster.appendRow(["Kode Barang", "Nama Barang", "Kategori", "Stok Awal", "Satuan", "Lokasi Rak"]);
sheetMaster.getRange("A1:F1").setBackground("#4a86e8").setFontColor("white").setFontWeight("bold");
}
// 2. Sheet Transaksi
let sheetTransaksi = ss.getSheetByName("Transaksi");
if (!sheetTransaksi) {
sheetTransaksi = ss.insertSheet("Transaksi");
sheetTransaksi.appendRow(["ID Transaksi", "Tanggal", "Kode Barang", "Jenis", "Jumlah", "Keterangan"]);
sheetTransaksi.getRange("A1:F1").setBackground("#e6b8af").setFontColor("black").setFontWeight("bold");
}
return "Database Berhasil Disiapkan!";
}
// Fungsi Login Sederhana
function checkLogin(pass) {
return pass === APP_PASSWORD;
}
// Mengambil Data untuk Dashboard
function getDashboardStats() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const master = ss.getSheetByName("MasterBarang").getDataRange().getValues();
const trans = ss.getSheetByName("Transaksi").getDataRange().getValues();
const totalBarang = master.length - 1;
let masuk = 0;
let keluar = 0;
for (let i = 1; i < trans.length; i++) {
if (trans[i][3] === "Masuk") masuk += Number(trans[i][4]);
if (trans[i][3] === "Keluar") keluar += Number(trans[i][4]);
}
return {
totalJenis: totalBarang > 0 ? totalBarang : 0,
totalMasuk: masuk,
totalKeluar: keluar
};
}
// Fungsi Master Barang
function getMasterBarang() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("MasterBarang");
const data = sheet.getDataRange().getValues();
const headers = data.shift();
// Hitung stok aktual (Stok Awal + Masuk - Keluar)
const transSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Transaksi");
const transData = transSheet.getDataRange().getValues();
transData.shift();
return data.map(row => {
const kode = row[0];
const stokAwal = Number(row[3]);
let totalMasuk = 0;
let totalKeluar = 0;
transData.forEach(t => {
if (t[2] === kode) {
if (t[3] === "Masuk") totalMasuk += Number(t[4]);
if (t[3] === "Keluar") totalKeluar += Number(t[4]);
}
});
return {
kode: row[0],
nama: row[1],
kategori: row[2],
stokAwal: stokAwal,
satuan: row[4],
lokasi: row[5],
stokAkhir: (stokAwal + totalMasuk - totalKeluar)
};
});
}
function addMasterBarang(obj) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("MasterBarang");
sheet.appendRow([obj.kode, obj.nama, obj.kategori, obj.stokAwal, obj.satuan, obj.lokasi]);
return true;
}
// Fungsi Transaksi
function getTransaksi() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Transaksi");
const data = sheet.getDataRange().getValues();
data.shift();
return data.map(row => ({
id: row[0],
tanggal: Utilities.formatDate(new Date(row[1]), "GMT+7", "dd/MM/yyyy HH:mm"),
kode: row[2],
jenis: row[3],
jumlah: row[4],
ket: row[5]
})).reverse(); // Terbaru di atas
}
function addTransaksi(obj) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Transaksi");
const id = "TRX-" + new Date().getTime();
sheet.appendRow([id, new Date(), obj.kode, obj.jenis, obj.jumlah, obj.ket]);
return true;
}
Index.html
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inventaris App</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style>
.loading-overlay {
display: none;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(255,255,255,0.8);
z-index: 9999;
justify-content: center;
align-items: center;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div id="loading" class="loading-overlay">
<div class="text-center">
<i class="fas fa-circle-notch fa-spin text-4xl text-blue-600 mb-2"></i>
<p class="font-semibold">Memproses...</p>
</div>
</div>
<!-- LOGIN SCREEN -->
<div id="login-screen" class="flex items-center justify-center min-h-screen">
<div class="bg-white p-8 rounded-xl shadow-lg w-full max-w-md">
<div class="text-center mb-8">
<i class="fas fa-boxes-stacked text-5xl text-blue-600 mb-4"></i>
<h1 class="text-2xl font-bold text-gray-800">Inventaris Barang</h1>
<p class="text-gray-500">Silahkan login untuk melanjutkan</p>
</div>
<div class="space-y-4">
<input type="password" id="login-pass" placeholder="Password Admin" class="w-full px-4 py-3 border rounded-lg focus:ring-2 focus:ring-blue-400 outline-none">
<button onclick="handleLogin()" class="w-full bg-blue-600 text-white py-3 rounded-lg font-bold hover:bg-blue-700 transition">LOGIN</button>
<p id="login-error" class="text-red-500 text-sm text-center hidden">Password salah!</p>
</div>
</div>
</div>
<!-- MAIN APP SCREEN (HIDDEN) -->
<div id="app-screen" class="hidden flex flex-col md:flex-row min-h-screen">
<!-- Sidebar -->
<div class="w-full md:w-64 bg-slate-800 text-white flex-shrink-0">
<div class="p-6 text-xl font-bold border-b border-slate-700 flex items-center gap-2">
<i class="fas fa-warehouse"></i> MyStock
</div>
<nav class="p-4 space-y-2">
<button onclick="showPage('dashboard')" class="w-full text-left px-4 py-2 rounded hover:bg-slate-700 flex items-center gap-3 transition">
<i class="fas fa-chart-line w-5"></i> Dashboard
</button>
<button onclick="showPage('barang')" class="w-full text-left px-4 py-2 rounded hover:bg-slate-700 flex items-center gap-3 transition">
<i class="fas fa-list w-5"></i> Daftar Barang
</button>
<button onclick="showPage('masuk')" class="w-full text-left px-4 py-2 rounded hover:bg-slate-700 flex items-center gap-3 transition">
<i class="fas fa-arrow-right w-5 text-green-400"></i> Barang Masuk
</button>
<button onclick="showPage('keluar')" class="w-full text-left px-4 py-2 rounded hover:bg-slate-700 flex items-center gap-3 transition">
<i class="fas fa-arrow-left w-5 text-red-400"></i> Barang Keluar
</button>
<div class="pt-4 border-t border-slate-700">
<button onclick="location.reload()" class="w-full text-left px-4 py-2 rounded hover:bg-red-600 flex items-center gap-3 transition">
<i class="fas fa-sign-out-alt w-5"></i> Logout
</button>
</div>
</nav>
</div>
<!-- Main Content -->
<main class="flex-grow p-4 md:p-8 overflow-y-auto">
<!-- DASHBOARD PAGE -->
<div id="page-dashboard" class="page-content">
<h2 class="text-2xl font-bold mb-6">Ringkasan Gudang</h2>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-6">
<div class="bg-white p-6 rounded-xl shadow-sm border-l-4 border-blue-500">
<p class="text-gray-500 text-sm uppercase font-bold">Total Jenis Barang</p>
<h3 id="stat-jenis" class="text-3xl font-bold">0</h3>
</div>
<div class="bg-white p-6 rounded-xl shadow-sm border-l-4 border-green-500">
<p class="text-gray-500 text-sm uppercase font-bold">Total Qty Masuk</p>
<h3 id="stat-masuk" class="text-3xl font-bold text-green-600">0</h3>
</div>
<div class="bg-white p-6 rounded-xl shadow-sm border-l-4 border-red-500">
<p class="text-gray-500 text-sm uppercase font-bold">Total Qty Keluar</p>
<h3 id="stat-keluar" class="text-3xl font-bold text-red-600">0</h3>
</div>
</div>
<div class="mt-8 bg-white p-6 rounded-xl shadow-sm">
<h3 class="font-bold mb-4">Transaksi Terakhir</h3>
<div class="overflow-x-auto">
<table class="w-full text-left">
<thead class="bg-gray-50 text-gray-600 text-sm">
<tr>
<th class="p-3">Tanggal</th>
<th class="p-3">Kode</th>
<th class="p-3">Tipe</th>
<th class="p-3 text-right">Jumlah</th>
</tr>
</thead>
<tbody id="dash-trans-list" class="divide-y">
<!-- Data inject -->
</tbody>
</table>
</div>
</div>
</div>
<!-- DAFTAR BARANG PAGE -->
<div id="page-barang" class="page-content hidden">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold">Data Master Barang</h2>
<button onclick="toggleModal('modal-barang', true)" class="bg-blue-600 text-white px-4 py-2 rounded-lg hover:bg-blue-700">
<i class="fas fa-plus"></i> Tambah Barang
</button>
</div>
<div class="bg-white rounded-xl shadow-sm overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-left">
<thead class="bg-gray-800 text-white">
<tr>
<th class="p-3">Kode</th>
<th class="p-3">Nama</th>
<th class="p-3">Kategori</th>
<th class="p-3">Lokasi Rak</th>
<th class="p-3 text-center">Stok Sisa</th>
<th class="p-3">Satuan</th>
</tr>
</thead>
<tbody id="master-list" class="divide-y">
<!-- Data inject -->
</tbody>
</table>
</div>
</div>
</div>
<!-- TRANSAKSI PAGE (Masuk/Keluar) -->
<div id="page-masuk" class="page-content hidden">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold">Barang Masuk (Input)</h2>
</div>
<div class="bg-white p-6 rounded-xl shadow-md max-w-2xl mx-auto">
<form id="form-masuk" onsubmit="event.preventDefault(); submitTransaksi('Masuk')">
<div class="grid grid-cols-1 gap-4">
<div>
<label class="block text-sm font-bold mb-1">Pilih Barang</label>
<select id="input-m-kode" required class="w-full p-2 border rounded bg-gray-50"></select>
</div>
<div>
<label class="block text-sm font-bold mb-1">Jumlah Masuk</label>
<input type="number" id="input-m-qty" required min="1" class="w-full p-2 border rounded">
</div>
<div>
<label class="block text-sm font-bold mb-1">Keterangan / Supplier</label>
<textarea id="input-m-ket" class="w-full p-2 border rounded" rows="2"></textarea>
</div>
<button type="submit" class="bg-green-600 text-white py-3 rounded-lg font-bold hover:bg-green-700">SIMPAN BARANG MASUK</button>
</div>
</form>
</div>
</div>
<div id="page-keluar" class="page-content hidden">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold">Barang Keluar (Output)</h2>
</div>
<div class="bg-white p-6 rounded-xl shadow-md max-w-2xl mx-auto border-t-4 border-red-500">
<form id="form-keluar" onsubmit="event.preventDefault(); submitTransaksi('Keluar')">
<div class="grid grid-cols-1 gap-4">
<div>
<label class="block text-sm font-bold mb-1">Pilih Barang</label>
<select id="input-k-kode" required class="w-full p-2 border rounded bg-gray-50"></select>
</div>
<div>
<label class="block text-sm font-bold mb-1">Jumlah Keluar</label>
<input type="number" id="input-k-qty" required min="1" class="w-full p-2 border rounded">
</div>
<div>
<label class="block text-sm font-bold mb-1">Tujuan / Keterangan</label>
<textarea id="input-k-ket" class="w-full p-2 border rounded" rows="2"></textarea>
</div>
<button type="submit" class="bg-red-600 text-white py-3 rounded-lg font-bold hover:bg-red-700">SIMPAN BARANG KELUAR</button>
</div>
</form>
</div>
</div>
</main>
</div>
<!-- MODAL TAMBAH BARANG -->
<div id="modal-barang" class="fixed inset-0 bg-black/50 hidden flex items-center justify-center p-4 z-50">
<div class="bg-white rounded-xl w-full max-w-lg overflow-hidden">
<div class="p-4 bg-blue-600 text-white font-bold flex justify-between">
<span>Tambah Master Barang</span>
<button onclick="toggleModal('modal-barang', false)"><i class="fas fa-times"></i></button>
</div>
<form id="form-add-barang" onsubmit="event.preventDefault(); submitAddBarang()" class="p-6 grid grid-cols-1 gap-4">
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-bold text-gray-500">KODE BARANG</label>
<input type="text" id="add-kode" required class="w-full p-2 border rounded uppercase">
</div>
<div>
<label class="block text-xs font-bold text-gray-500">NAMA BARANG</label>
<input type="text" id="add-nama" required class="w-full p-2 border rounded">
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-bold text-gray-500">KATEGORI</label>
<input type="text" id="add-kategori" placeholder="Elektronik, ATK, dll" class="w-full p-2 border rounded">
</div>
<div>
<label class="block text-xs font-bold text-gray-500">SATUAN</label>
<input type="text" id="add-satuan" placeholder="Pcs, Box, Rim" class="w-full p-2 border rounded">
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-xs font-bold text-gray-500">STOK AWAL</label>
<input type="number" id="add-stok" value="0" required class="w-full p-2 border rounded">
</div>
<div>
<label class="block text-xs font-bold text-gray-500">LOKASI RAK</label>
<input type="text" id="add-lokasi" placeholder="RAK-A1" class="w-full p-2 border rounded uppercase">
</div>
</div>
<div class="flex gap-2 pt-4">
<button type="button" onclick="toggleModal('modal-barang', false)" class="flex-1 py-2 border rounded hover:bg-gray-50">Batal</button>
<button type="submit" class="flex-1 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">Simpan Barang</button>
</div>
</form>
</div>
</div>
<script>
let isAuth = false;
function showLoading(show) {
document.getElementById('loading').style.display = show ? 'flex' : 'none';
}
function toggleModal(id, show) {
document.getElementById(id).classList.toggle('hidden', !show);
}
function showPage(pageId) {
document.querySelectorAll('.page-content').forEach(p => p.classList.add('hidden'));
document.getElementById('page-' + pageId).classList.remove('hidden');
loadData(pageId);
}
function handleLogin() {
const pass = document.getElementById('login-pass').value;
showLoading(true);
google.script.run.withSuccessHandler(res => {
showLoading(false);
if(res) {
isAuth = true;
document.getElementById('login-screen').classList.add('hidden');
document.getElementById('app-screen').classList.remove('hidden');
showPage('dashboard');
} else {
document.getElementById('login-error').classList.remove('hidden');
}
}).checkLogin(pass);
}
function loadData(page) {
showLoading(true);
if(page === 'dashboard') {
google.script.run.withSuccessHandler(stats => {
document.getElementById('stat-jenis').innerText = stats.totalJenis;
document.getElementById('stat-masuk').innerText = stats.totalMasuk;
document.getElementById('stat-keluar').innerText = stats.totalKeluar;
google.script.run.withSuccessHandler(trans => {
const tbody = document.getElementById('dash-trans-list');
tbody.innerHTML = trans.slice(0, 5).map(t => `
<tr>
<td class="p-3 text-xs">${t.tanggal}</td>
<td class="p-3">${t.kode}</td>
<td class="p-3"><span class="px-2 py-1 text-xs rounded ${t.jenis === 'Masuk' ? 'bg-green-100 text-green-700':'bg-red-100 text-red-700'}">${t.jenis}</span></td>
<td class="p-3 text-right font-bold">${t.jumlah}</td>
</tr>
`).join('');
showLoading(false);
}).getTransaksi();
}).getDashboardStats();
}
if(page === 'barang') {
google.script.run.withSuccessHandler(data => {
const tbody = document.getElementById('master-list');
tbody.innerHTML = data.map(item => `
<tr class="hover:bg-gray-50">
<td class="p-3 font-mono text-sm">${item.kode}</td>
<td class="p-3 font-semibold">${item.nama}</td>
<td class="p-3 text-sm text-gray-600">${item.kategori}</td>
<td class="p-3"><span class="bg-gray-100 px-2 py-1 rounded text-xs">${item.lokasi}</span></td>
<td class="p-3 text-center">
<span class="font-bold ${item.stokAkhir <= 5 ? 'text-red-600':'text-blue-600'}">${item.stokAkhir}</span>
</td>
<td class="p-3 text-sm">${item.satuan}</td>
</tr>
`).join('');
showLoading(false);
}).getMasterBarang();
}
if(page === 'masuk' || page === 'keluar') {
google.script.run.withSuccessHandler(data => {
const selectM = document.getElementById('input-m-kode');
const selectK = document.getElementById('input-k-kode');
const options = '<option value="">-- Pilih Barang --</option>' +
data.map(i => `<option value="${i.kode}">${i.kode} - ${i.nama}</option>`).join('');
selectM.innerHTML = options;
selectK.innerHTML = options;
showLoading(false);
}).getMasterBarang();
}
}
function submitAddBarang() {
const data = {
kode: document.getElementById('add-kode').value.toUpperCase(),
nama: document.getElementById('add-nama').value,
kategori: document.getElementById('add-kategori').value,
satuan: document.getElementById('add-satuan').value,
stokAwal: document.getElementById('add-stok').value,
lokasi: document.getElementById('add-lokasi').value.toUpperCase()
};
showLoading(true);
google.script.run.withSuccessHandler(() => {
document.getElementById('form-add-barang').reset();
toggleModal('modal-barang', false);
showPage('barang');
}).addMasterBarang(data);
}
function submitTransaksi(jenis) {
const prefix = jenis === 'Masuk' ? 'm' : 'k';
const data = {
kode: document.getElementById(`input-${prefix}-kode`).value,
jumlah: document.getElementById(`input-${prefix}-qty`).value,
ket: document.getElementById(`input-${prefix}-ket`).value,
jenis: jenis
};
if(!data.kode) return alert('Pilih barang terlebih dahulu!');
showLoading(true);
google.script.run.withSuccessHandler(() => {
document.getElementById(`form-${jenis.toLowerCase()}`).reset();
alert(`Data Barang ${jenis} Berhasil Disimpan!`);
showPage('dashboard');
}).addTransaksi(data);
}
</script>
</body>
</html>


Tidak ada komentar:
Posting Komentar