Upload files to "/"

This commit is contained in:
2026-10-28 21:37:23 -04:00
commit 00af64f859
3 changed files with 2543 additions and 0 deletions
+476
View File
@@ -0,0 +1,476 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Portal Resmi Pemasok & Pengiriman Barang</title>
<style>
:root {
--primary: #0288d1;
--primary-dark: #01579b;
--bg: #f4f9fd;
--border: #cfe2ff;
--success: #2e7d32;
--warning: #ed6c02;
--danger: #c62828;
--purple: #880e4f;
}
* { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
body { background-color: var(--bg); color: #212529; line-height: 1.5; padding: 20px 14px; }
.hidden { display: none !important; }
.container { max-width: 900px; margin: 0 auto; }
.card { background: #fff; border-radius: 14px; padding: 22px; border: 1px solid var(--border); box-shadow: 0 4px 14px rgba(2,136,209,0.06); margin-bottom: 20px; }
.header-box { display: flex; justify-content: space-between; align-items: center; border-bottom: 2px solid var(--primary); padding-bottom: 12px; margin-bottom: 16px; flex-wrap: wrap; gap: 8px; }
.form-group { margin-bottom: 12px; }
.form-group label { display: block; font-size: 13px; font-weight: bold; margin-bottom: 4px; }
.form-control { width: 100%; padding: 10px; border: 1.5px solid #ccc; border-radius: 6px; font-size: 14px; }
.form-control:focus { outline: none; border-color: var(--primary); }
.btn { padding: 9px 16px; border: none; border-radius: 6px; font-weight: bold; cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.btn-block { width: 100%; }
.btn-primary { background: var(--primary); color: #fff; }
.btn-success { background: var(--success); color: #fff; }
.btn-secondary { background: #eee; color: #333; font-size: 12px; padding: 6px 12px; }
.auth-box { max-width: 420px; margin: 50px auto; }
.auth-tabs { display: flex; gap: 8px; margin-bottom: 16px; border-bottom: 2px solid #eee; }
.auth-tab { flex: 1; padding: 10px; background: none; border: none; font-weight: bold; cursor: pointer; color: #777; }
.auth-tab.active { color: var(--primary); border-bottom: 3px solid var(--primary); margin-bottom: -2px; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 13px; min-width: 600px; }
th, td { padding: 10px; border: 1px solid #dee2e6; text-align: left; }
th { background: #e7f1ff; }
.pill { display: inline-block; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: bold; }
.pill-kirim { background: #fff3cd; color: #856404; }
.pill-terima { background: #d1e7dd; color: #0f5132; }
.badge-juragan { background: var(--purple); color: #fff; padding: 3px 8px; border-radius: 4px; font-size: 11px; font-weight: bold; }
</style>
</head>
<body>
<div class="container">
<!-- ================= MASUK & DAFTAR PEMASOK ================= -->
<div id="page-supplier-auth" class="card auth-box">
<div style="text-align: center; margin-bottom: 14px;">
<h2 style="color: var(--primary); margin-bottom: 4px;">🚚 Portal Pemasok</h2>
<small style="color: #666;">Mitra Pemasok, Kurir & Akses Pemilik Toko</small>
</div>
<div class="auth-tabs">
<button class="auth-tab active" id="tab-sup-login" onclick="gantiTabSupAuth('login')">Masuk</button>
<button class="auth-tab" id="tab-sup-reg" onclick="gantiTabSupAuth('reg')">Daftar Mitra</button>
</div>
<form id="form-sup-login" onsubmit="handleLoginSupplier(event)">
<div class="form-group">
<label>Nama Pengguna (Mitra atau Pemilik)</label>
<input type="text" id="log-sup-user" class="form-control" placeholder="Username pemasok / pemilik" required>
</div>
<div class="form-group">
<label>Kata Sandi</label>
<input type="password" id="log-sup-pass" class="form-control" placeholder="Kata sandi" required>
</div>
<button type="submit" class="btn btn-primary btn-block">Masuk ke Portal</button>
<div id="sup-auth-msg" style="margin-top: 10px; color: var(--danger); font-size: 13px; text-align: center; font-weight: bold;"></div>
<div style="text-align: center; margin-top: 14px;">
<a href="javascript:void(0)" onclick="bukaModalResetSupplier()" style="font-size: 12px; color: var(--primary); text-decoration: none; font-weight: 600;">Lupa Kata Sandi Pemasok?</a>
</div>
</form>
<form id="form-sup-reg" class="hidden" onsubmit="handleRegisterSupplier(event)">
<div class="form-group">
<label>Nama Perusahaan / PT / Distributor</label>
<input type="text" id="reg-sup-nama" class="form-control" placeholder="Contoh: CV Sumber Makmur" required>
</div>
<div class="form-group">
<label>Nomor Kontak / WhatsApp</label>
<input type="text" id="reg-sup-hp" class="form-control" placeholder="0812-xxxx-xxxx" required>
</div>
<div class="form-group">
<label>Nama Pengguna</label>
<input type="text" id="reg-sup-user" class="form-control" placeholder="Username akun" required>
</div>
<div class="form-group">
<label>Kata Sandi</label>
<input type="password" id="reg-sup-pass" class="form-control" placeholder="Kata sandi akun" required>
</div>
<button type="submit" class="btn btn-primary btn-block">Daftar Sebagai Mitra</button>
</form>
<div style="text-align: center; margin-top: 16px; border-top: 1px dashed #ddd; padding-top: 12px;">
<a href="index.html" style="font-size: 12px; color: #555; text-decoration: none;">← Kembali ke Halaman Utama Toko</a>
</div>
</div>
<!-- ================= DASHBOARD PEMASOK ================= -->
<div id="page-supplier-dashboard" class="hidden">
<div class="card">
<div class="header-box">
<div>
<h2 style="color: var(--primary); margin: 0;">🚚 Formulir Surat Jalan & Pengiriman Barang</h2>
<div style="margin-top: 4px;">
<span id="supplier-info-label" style="color: #555; font-weight: bold;"></span>
<span id="label-mode-juragan" class="badge-juragan hidden">👑 Akses Pemilik Toko</span>
</div>
</div>
<div style="display: flex; gap: 8px;">
<a href="index.html" class="btn-secondary">← Kembali ke Toko</a>
<button class="btn btn-secondary" onclick="logoutSupplier()">Keluar</button>
</div>
</div>
<form onsubmit="handleKirimBarang(event)">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px;">
<div class="form-group">
<label>Nama Mitra / Pemasok</label>
<input type="text" id="kirim-sup-nama" class="form-control" required>
</div>
<div class="form-group">
<label>Nomor Kontak</label>
<input type="text" id="kirim-sup-hp" class="form-control" required>
</div>
<div class="form-group">
<label>Nama Barang yang Dikirim</label>
<input type="text" id="kirim-barang" class="form-control" placeholder="Contoh: Kopi Hitam Gula Aren" required>
</div>
<div class="form-group">
<label>Jumlah Pasokan (Unit / Dus)</label>
<input type="number" id="kirim-qty" class="form-control" placeholder="Contoh: 50" min="1" required>
</div>
<div class="form-group">
<label>Harga Beli Satuan (Rp)</label>
<input type="number" id="kirim-harga" class="form-control" placeholder="Contoh: 8000" required>
</div>
<div class="form-group">
<label>No. Plat Kendaraan / Nama Kurir</label>
<input type="text" id="kirim-kurir" class="form-control" placeholder="B 1234 ABC (Pak Doni)" required>
</div>
</div>
<button type="submit" class="btn btn-primary" style="margin-top: 10px;">🚀 Catat & Kirim Data Pasokan</button>
</form>
</div>
<div class="card">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">
<h3 id="table-title">📦 Riwayat Surat Jalan & Pengiriman</h3>
<span id="filter-keterangan" style="font-size: 12px; color: #666;"></span>
</div>
<div style="overflow-x: auto;">
<table>
<thead>
<tr>
<th>No. Resi</th>
<th>Pemasok / Sales</th>
<th>Nama Barang</th>
<th>Jumlah</th>
<th>Total Tagihan</th>
<th>Kurir</th>
<th>Status</th>
<th id="kolom-aksi-juragan">Aksi</th>
</tr>
</thead>
<tbody id="supplier-table-body"></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Modal Reset Password Supplier -->
<div id="modal-reset-sup" class="modal-overlay hidden">
<div class="modal-content">
<h3 style="color: var(--primary); margin-bottom: 10px;">🔑 Reset Kata Sandi Pemasok</h3>
<form onsubmit="handleResetPasswordSupplier(event)">
<div class="form-group">
<label>Nama Pengguna (Username)</label>
<input type="text" id="reset-sup-user" class="form-control" placeholder="Masukkan username pemasok" required>
</div>
<div class="form-group">
<label>Kata Sandi Baru</label>
<input type="password" id="reset-sup-pass" class="form-control" placeholder="Masukkan kata sandi baru" required>
</div>
<div class="form-group">
<label>Konfirmasi Kata Sandi Baru</label>
<input type="password" id="reset-sup-confirm" class="form-control" placeholder="Ulangi kata sandi baru" required>
</div>
<div style="display: flex; gap: 8px; margin-top: 14px;">
<button type="button" class="btn btn-secondary" style="width: 50%;" onclick="tutupModalResetSupplier()">Batal</button>
<button type="submit" class="btn btn-primary" style="width: 50%;">Simpan Sandi</button>
</div>
</form>
</div>
</div>
<script>
function bukaModalResetSupplier() {
document.getElementById("modal-reset-sup").classList.remove("hidden");
}
function tutupModalResetSupplier() {
document.getElementById("modal-reset-sup").classList.add("hidden");
}
function handleResetPasswordSupplier(e) {
e.preventDefault();
const username = document.getElementById("reset-sup-user").value.trim();
const sandiBaru = document.getElementById("reset-sup-pass").value;
const konfirmasi = document.getElementById("reset-sup-confirm").value;
if (sandiBaru !== konfirmasi) {
return alert("Kata sandi baru dan konfirmasi tidak cocok!");
}
let dbAkunSupplier = getStorage("db_akun_supplier", []);
const akun = dbAkunSupplier.find(s => s.user.toLowerCase() === username.toLowerCase());
if (!akun) {
return alert("Username pemasok tidak ditemukan dalam sistem!");
}
akun.pass = sandiBaru;
setStorage("db_akun_supplier", dbAkunSupplier);
alert(`Kata sandi akun @${username} berhasil diperbarui! Silakan masuk.`);
tutupModalResetSupplier();
e.target.reset();
}
function getStorage(k, fb) { const d = localStorage.getItem(k); return d ? JSON.parse(d) : fb; }
function setStorage(k, v) { localStorage.setItem(k, JSON.stringify(v)); }
let dbAkunSupplier = getStorage("db_akun_supplier", [
{ nama: "CV Sumber Kopi Abadi", hp: "0812-3456-7890", user: "supplier", pass: "123" }
]);
let dbSupplierPengiriman = getStorage("db_supplier_pengiriman", []);
let dbAkunToko = getStorage("db_akun", []);
let dbBarangToko = getStorage("db_barang", []);
let sesiSupplier = getStorage("aktif_supplier", null);
let sesiToko = getStorage("aktif_user", null);
window.onload = function() {
if (sesiToko && sesiToko.role === "admin") {
sesiSupplier = {
nama: sesiToko.name,
hp: "Kantor Pusat",
user: sesiToko.user,
role: "admin"
};
setStorage("aktif_supplier", sesiSupplier);
bukaDashboardSupplier();
return;
}
if (sesiSupplier) {
bukaDashboardSupplier();
} else {
document.getElementById("page-supplier-auth").classList.remove("hidden");
document.getElementById("page-supplier-dashboard").classList.add("hidden");
}
};
function gantiTabSupAuth(tab) {
document.getElementById("tab-sup-login").classList.toggle("active", tab === 'login');
document.getElementById("tab-sup-reg").classList.toggle("active", tab === 'reg');
document.getElementById("form-sup-login").classList.toggle("hidden", tab !== 'login');
document.getElementById("form-sup-reg").classList.toggle("hidden", tab !== 'reg');
document.getElementById("sup-auth-msg").innerText = "";
}
function handleRegisterSupplier(e) {
e.preventDefault();
const nama = document.getElementById("reg-sup-nama").value.trim();
const hp = document.getElementById("reg-sup-hp").value.trim();
const user = document.getElementById("reg-sup-user").value.trim();
const pass = document.getElementById("reg-sup-pass").value;
if (dbAkunSupplier.some(s => s.user.toLowerCase() === user.toLowerCase())) {
return document.getElementById("sup-auth-msg").innerText = "Nama pengguna sudah digunakan!";
}
dbAkunSupplier.push({ nama, hp, user, pass, role: "supplier" });
setStorage("db_akun_supplier", dbAkunSupplier);
alert("Akun mitra berhasil didaftarkan! Silakan masuk.");
gantiTabSupAuth('login');
document.getElementById("form-sup-reg").reset();
}
function handleLoginSupplier(e) {
e.preventDefault();
const user = document.getElementById("log-sup-user").value.trim();
const pass = document.getElementById("log-sup-pass").value;
dbAkunToko = getStorage("db_akun", []);
const juraganMatch = dbAkunToko.find(u => u.user === user && u.pass === pass && u.role === "admin");
if (juraganMatch) {
sesiSupplier = {
nama: juraganMatch.name,
hp: "Kantor Pemilik",
user: juraganMatch.user,
role: "admin"
};
setStorage("aktif_supplier", sesiSupplier);
bukaDashboardSupplier();
return;
}
const match = dbAkunSupplier.find(s => s.user === user && s.pass === pass);
if (!match) {
return document.getElementById("sup-auth-msg").innerText = "Nama pengguna atau kata sandi salah!";
}
sesiSupplier = { ...match, role: "supplier" };
setStorage("aktif_supplier", sesiSupplier);
bukaDashboardSupplier();
}
function logoutSupplier() {
sesiSupplier = null;
localStorage.removeItem("aktif_supplier");
document.getElementById("page-supplier-dashboard").classList.add("hidden");
document.getElementById("page-supplier-auth").classList.remove("hidden");
}
function bukaDashboardSupplier() {
document.getElementById("page-supplier-auth").classList.add("hidden");
document.getElementById("page-supplier-dashboard").classList.remove("hidden");
const isJuragan = sesiSupplier.role === "admin";
document.getElementById("label-mode-juragan").classList.toggle("hidden", !isJuragan);
if (isJuragan) {
document.getElementById("supplier-info-label").innerText = `Masuk sebagai: Pemilik Toko (${sesiSupplier.nama})`;
document.getElementById("kirim-sup-nama").value = "Pusat / Pemilik Toko";
document.getElementById("kirim-sup-hp").value = "0811-Admin-Pusat";
document.getElementById("kirim-sup-nama").removeAttribute("disabled");
document.getElementById("kirim-sup-hp").removeAttribute("disabled");
document.getElementById("table-title").innerText = "📦 Semua Pengiriman Pemasok (Akses Pemilik)";
document.getElementById("filter-keterangan").innerText = "Pemilik dapat langsung menerima dan memverifikasi barang.";
} else {
document.getElementById("supplier-info-label").innerText = `Mitra: ${sesiSupplier.nama} (@${sesiSupplier.user})`;
document.getElementById("kirim-sup-nama").value = sesiSupplier.nama;
document.getElementById("kirim-sup-hp").value = sesiSupplier.hp;
document.getElementById("kirim-sup-nama").setAttribute("disabled", "true");
document.getElementById("kirim-sup-hp").setAttribute("disabled", "true");
document.getElementById("table-title").innerText = "📦 Riwayat Pengiriman Saya";
document.getElementById("filter-keterangan").innerText = "Hanya menampilkan data pengiriman akun ini.";
}
renderTableKiriman();
}
function renderTableKiriman() {
const tbody = document.getElementById("supplier-table-body");
tbody.innerHTML = "";
dbSupplierPengiriman = getStorage("db_supplier_pengiriman", []);
const isJuragan = sesiSupplier.role === "admin";
const dataDitampilake = isJuragan
? dbSupplierPengiriman
: dbSupplierPengiriman.filter(s => s.supplierUser === sesiSupplier.user);
if (dataDitampilake.length === 0) {
tbody.innerHTML = `<tr><td colspan="8" style="text-align:center;">Belum ada riwayat pengiriman barang.</td></tr>`;
return;
}
dataDitampilake.slice().reverse().forEach(s => {
let pill = s.status === "Diterima" ? "pill-terima" : "pill-kirim";
let total = parseInt(s.jumlahStok, 10) * parseInt(s.hargaBeli, 10);
let aksiHtml = "-";
if (isJuragan) {
aksiHtml = s.status === "Dikirim"
? `<button class="btn btn-success btn-sm" onclick="terimaKirimanLangsung(${s.id})">Terima & Tambah Stok</button>`
: `<span style="color:green; font-weight:bold;">✓ Selesai</span>`;
} else {
aksiHtml = s.status === "Diterima" ? `<span style="color:green;">✓ Diterima</span>` : `⏳ Menunggu Verifikasi`;
}
tbody.innerHTML += `
<tr>
<td><code>#${s.resi}</code></td>
<td><strong>${s.namaSupplier}</strong><br><small>${s.salesHp}</small></td>
<td>${s.namaBarang}</td>
<td><strong>${s.jumlahStok}</strong> unit</td>
<td>Rp ${total.toLocaleString("id-ID")}</td>
<td>${s.kurir}</td>
<td><span class="pill ${pill}">${s.status === 'Diterima' ? '✓ Diterima Pemilik' : '⏳ Dalam Pengiriman'}</span></td>
<td>${aksiHtml}</td>
</tr>
`;
});
}
function handleKirimBarang(e) {
e.preventDefault();
const resiBaru = "SJ-" + Math.floor(100000 + Math.random() * 900000);
const dataKiriman = {
id: Date.now(),
resi: resiBaru,
supplierUser: sesiSupplier.user,
namaSupplier: document.getElementById("kirim-sup-nama").value.trim(),
salesHp: document.getElementById("kirim-sup-hp").value.trim(),
namaBarang: document.getElementById("kirim-barang").value.trim(),
jumlahStok: parseInt(document.getElementById("kirim-qty").value, 10),
hargaBeli: parseInt(document.getElementById("kirim-harga").value, 10),
kurir: document.getElementById("kirim-kurir").value.trim(),
status: "Dikirim"
};
dbSupplierPengiriman.push(dataKiriman);
setStorage("db_supplier_pengiriman", dbSupplierPengiriman);
alert(`Pengiriman berhasil dicatat dengan Resi #${resiBaru}!`);
document.getElementById("kirim-barang").value = "";
document.getElementById("kirim-qty").value = "";
document.getElementById("kirim-harga").value = "";
document.getElementById("kirim-kurir").value = "";
renderTableKiriman();
}
function terimaKirimanLangsung(id) {
dbBarangToko = getStorage("db_barang", []);
dbSupplierPengiriman = getStorage("db_supplier_pengiriman", []);
const s = dbSupplierPengiriman.find(x => x.id === id);
if (!s) return;
const totalBiaya = parseInt(s.jumlahStok, 10) * parseInt(s.hargaBeli, 10);
let b = dbBarangToko.find(x => x.nama.toLowerCase() === s.namaBarang.toLowerCase());
if (b) {
b.stok += parseInt(s.jumlahStok, 10);
} else {
dbBarangToko.push({
id: Date.now(),
nama: s.namaBarang,
harga: parseInt(s.hargaBeli, 10) * 1.2,
stok: parseInt(s.jumlahStok, 10),
stokMin: 5,
icon: "📦"
});
}
s.status = "Diterima";
setStorage("db_supplier_pengiriman", dbSupplierPengiriman);
setStorage("db_barang", dbBarangToko);
renderTableKiriman();
alert(`Pengiriman #${s.resi} berhasil diterima!\n- Stok "${s.namaBarang}" bertambah ${s.jumlahStok} unit.\n- Biaya pembelian Rp ${totalBiaya.toLocaleString("id-ID")} otomatis memotong saldo kas bank toko.`);
}
</script>
</body>
</html>