Files
omahcitra-store/js/app.js

251 lines
8.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ============================================================
OmahCitra Store - Aplikasi (jQuery)
Fitur: Katalog, Keranjang, Checkout, Pengiriman, Pembayaran
============================================================ */
$(function () {
"use strict";
// ---------- State ----------
const cart = {}; // { id: qty }
let activeCat = "Semua";
let searchTerm = "";
let selectedCourier = null;
let selectedPayment = null;
// ---------- Helpers ----------
const rupiah = (n) => "Rp" + Number(n).toLocaleString("id-ID");
const $view = (name) => {
$(".view").removeClass("active");
$("#view-" + name).addClass("active");
window.scrollTo(0, 0);
};
const toast = (msg) => {
const $t = $("#toast").text(msg).addClass("show");
clearTimeout(toast._t);
toast._t = setTimeout(() => $t.removeClass("show"), 1800);
};
// ---------- Category nav ----------
function buildCats() {
const cats = ["Semua", ...new Set(PRODUCTS.map(p => p.category))];
const $nav = $("#catNav").empty();
cats.forEach(c => {
$("<div>")
.addClass("cat-pill" + (c === activeCat ? " active" : ""))
.text(c)
.on("click", function () {
activeCat = c;
buildCats();
renderCatalog();
})
.appendTo($nav);
});
}
// ---------- Catalog ----------
function renderCatalog() {
const grid = $("#productGrid").empty();
const term = searchTerm.toLowerCase();
const list = PRODUCTS.filter(p => {
const okCat = activeCat === "Semua" || p.category === activeCat;
const okSearch = !term ||
p.name.toLowerCase().includes(term) ||
p.category.toLowerCase().includes(term) ||
p.desc.toLowerCase().includes(term);
return okCat && okSearch;
});
$("#emptyNote").toggle(list.length === 0);
list.forEach(p => {
const card = $(`
<div class="product-card">
<div class="pc-thumb">${p.emoji}</div>
<div class="pc-body">
<span class="pc-cat">${p.category}${p.badge ? `<span class="pc-badge">${p.badge}</span>` : ""}</span>
<div class="pc-name">${p.name}</div>
<div class="pc-rating">★ ${p.rating} · ${p.sold.toLocaleString("id-ID")} terjual</div>
<div class="pc-price">${rupiah(p.price)}</div>
<button class="pc-btn" data-id="${p.id}">+ Keranjang</button>
</div>
</div>`);
card.find(".pc-btn").on("click", () => addToCart(p.id));
card.appendTo(grid);
});
}
// ---------- Cart ops ----------
function addToCart(id) {
cart[id] = (cart[id] || 0) + 1;
updateCartCount();
toast("Ditambahkan ke keranjang");
}
function setQty(id, qty) {
if (qty <= 0) delete cart[id];
else cart[id] = qty;
updateCartCount();
renderCart();
}
function updateCartCount() {
const n = Object.values(cart).reduce((a, b) => a + b, 0);
$("#cartCount").text(n);
}
function cartSubtotal() {
return Object.entries(cart).reduce((sum, [id, q]) => {
const p = PRODUCTS.find(x => x.id === id);
return sum + (p ? p.price * q : 0);
}, 0);
}
// ---------- Render cart ----------
function renderCart() {
const $list = $("#cartItems").empty();
const ids = Object.keys(cart);
if (ids.length === 0) {
$("#cartEmpty").show();
$("#cartSummary").hide();
return;
}
$("#cartEmpty").hide();
$("#cartSummary").show();
ids.forEach(id => {
const p = PRODUCTS.find(x => x.id === id);
const qty = cart[id];
const row = $(`
<div class="cart-row">
<div class="cart-emoji">${p.emoji}</div>
<div class="cart-info">
<h4>${p.name}</h4>
<small>${rupiah(p.price)} / pcs</small>
</div>
<div class="qty">
<button class="dec"></button>
<span>${qty}</span>
<button class="inc">+</button>
</div>
<div class="price">${rupiah(p.price * qty)}</div>
<button class="rm" title="Hapus">🗑️</button>
</div>`);
row.find(".inc").on("click", () => setQty(id, qty + 1));
row.find(".dec").on("click", () => setQty(id, qty - 1));
row.find(".rm").on("click", () => { delete cart[id]; updateCartCount(); renderCart(); toast("Dihapus"); });
row.appendTo($list);
});
$("#sumSubtotal").text(rupiah(cartSubtotal()));
}
// ---------- Checkout ----------
function buildCheckout() {
// couriers
const $c = $("#courierOptions").empty();
COURIERS.forEach(co => {
const opt = $(`
<label class="opt">
<input type="radio" name="courier" value="${co.id}">
<span>${co.name} · ${rupiah(co.fee)}</span>
<small>(${co.etd})</small>
</label>`);
opt.find("input").on("change", () => {
selectedCourier = co;
$(".opt", $c).removeClass("selected");
opt.addClass("selected");
updateOrderSummary();
});
opt.appendTo($c);
});
// payments
const $p = $("#paymentOptions").empty();
PAYMENTS.forEach(pm => {
const opt = $(`
<label class="opt">
<input type="radio" name="payment" value="${pm.id}">
<span>${pm.name}</span>
</label>`);
opt.find("input").on("change", () => {
selectedPayment = pm;
$(".opt", $p).removeClass("selected");
opt.addClass("selected");
});
opt.appendTo($p);
});
renderOrderItems();
updateOrderSummary();
}
function renderOrderItems() {
const $o = $("#orderItems").empty();
Object.entries(cart).forEach(([id, q]) => {
const p = PRODUCTS.find(x => x.id === id);
$(`<div class="order-item"><span>${p.emoji} ${p.name} ×${q}</span><span>${rupiah(p.price * q)}</span></div>`).appendTo($o);
});
}
function updateOrderSummary() {
const sub = cartSubtotal();
const ship = selectedCourier ? selectedCourier.fee : 0;
$("#coSubtotal").text(rupiah(sub));
$("#coShipping").text(selectedCourier ? rupiah(ship) : "Pilih kurir");
$("#coTotal").text(rupiah(sub + ship));
}
// ---------- Place order ----------
function placeOrder() {
if (Object.keys(cart).length === 0) { toast("Keranjang kosong"); return; }
const name = $("#custName").val().trim();
const addr = $("#custAddr").val().trim();
const phone = $("#custPhone").val().trim();
if (!name || !addr || !phone) { toast("Lengkapi data penerima"); return; }
if (!selectedCourier) { toast("Pilih kurir pengiriman"); return; }
if (!selectedPayment) { toast("Pilih metode pembayaran"); return; }
const orderNo = "OC" + Date.now().toString().slice(-8);
const sub = cartSubtotal();
const total = sub + selectedCourier.fee;
$("#orderNo").text(orderNo);
$("#doneDetail").html(`
<div><strong>Nama:</strong> ${name}</div>
<div><strong>Alamat:</strong> ${addr}</div>
<div><strong>HP:</strong> ${phone}</div>
<div><strong>Kurir:</strong> ${selectedCourier.name} (${selectedCourier.etd})</div>
<div><strong>Pembayaran:</strong> ${selectedPayment.name}</div>
<div><strong>Total:</strong> ${rupiah(total)}</div>
`);
// reset
for (const k in cart) delete cart[k];
updateCartCount();
selectedCourier = null; selectedPayment = null;
$("#custName,#custAddr,#custPhone").val("");
$view("done");
}
// ---------- Events ----------
$("#cartBtn").on("click", () => { renderCart(); $view("cart"); });
$("#toCheckout").on("click", () => {
if (Object.keys(cart).length === 0) { toast("Keranjang kosong"); return; }
buildCheckout(); $view("checkout");
});
$("#placeOrder").on("click", placeOrder);
$("#searchInput").on("input", function () { searchTerm = $(this).val(); renderCatalog(); });
// navigasi via data-view (brand & tombol)
$(document).on("click", "[data-view]", function (e) {
e.preventDefault();
const v = $(this).data("view");
if (v === "cart") renderCart();
if (v === "checkout") buildCheckout();
$view(v);
});
// ---------- Init ----------
buildCats();
renderCatalog();
updateCartCount();
});