OmahCitra store template: catalog, cart, checkout, shipping, payment (HTML/CSS/jQuery)
This commit is contained in:
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# OS / editor junk
|
||||||
|
.DS_Store
|
||||||
|
*.swp
|
||||||
|
*~
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
41
README.md
Normal file
41
README.md
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# OmahCitra Store
|
||||||
|
|
||||||
|
Template **toko online** statis (HTML + CSS + jQuery) — katalog produk terlaris
|
||||||
|
di Indonesia dengan keranjang belanja, checkout, pengiriman, dan pembayaran.
|
||||||
|
|
||||||
|
> Built as a rebuild of www.omahcitra.com into an online-store layout.
|
||||||
|
|
||||||
|
## Fitur
|
||||||
|
|
||||||
|
- **Katalog** — 12 produk paling laku (elektronik, fashion, kecantikan, makanan,
|
||||||
|
rumah tangga) dengan filter kategori & pencarian.
|
||||||
|
- **Shopping Cart** — tambah/hapus, ubah qty, subtotal real-time.
|
||||||
|
- **Checkout (Order)** — data penerima (nama, alamat, HP).
|
||||||
|
- **Pengiriman** — 4 kurir: JNE, SiCepat, POS, GoSend (ongkir + ETD).
|
||||||
|
- **Pembayaran** — 6 metode: QRIS, GoPay, OVO, DANA, Transfer Bank, COD.
|
||||||
|
- **Konfirmasi** — nomor pesanan + ringkasan order.
|
||||||
|
|
||||||
|
## Struktur
|
||||||
|
|
||||||
|
```
|
||||||
|
omahcitra-store/
|
||||||
|
├── index.html # halaman toko (semua view)
|
||||||
|
├── css/style.css # styling layout toko online
|
||||||
|
└── js/
|
||||||
|
├── jquery-3.7.1.min.js # jQuery (lokal, jalan offline)
|
||||||
|
├── products.js # data 12 produk + opsi kurir & pembayaran
|
||||||
|
└── app.js # logika jQuery: cart, checkout, shipping, payment
|
||||||
|
```
|
||||||
|
|
||||||
|
## Cara pakai
|
||||||
|
|
||||||
|
Buka `index.html` langsung di browser (double-click). jQuery sudah lokal, jadi
|
||||||
|
**tidak butuh internet**.
|
||||||
|
|
||||||
|
## Catatan
|
||||||
|
|
||||||
|
- Ini template front-end statis (simulasi order, tanpa backend/database).
|
||||||
|
- Data produk adalah contoh "paling laku di Indonesia", bukan data real-time.
|
||||||
|
|
||||||
|
## License
|
||||||
|
MIT — bebas dipakai & dimodifikasi.
|
||||||
168
css/style.css
Normal file
168
css/style.css
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/* ============================================================
|
||||||
|
OmahCitra Store - Stylesheet
|
||||||
|
============================================================ */
|
||||||
|
:root{
|
||||||
|
--primary:#e63946;
|
||||||
|
--primary-dark:#c1121f;
|
||||||
|
--accent:#ffb703;
|
||||||
|
--bg:#f6f7f9;
|
||||||
|
--card:#ffffff;
|
||||||
|
--text:#1d1d1f;
|
||||||
|
--muted:#6b7280;
|
||||||
|
--line:#e5e7eb;
|
||||||
|
--radius:14px;
|
||||||
|
--shadow:0 4px 18px rgba(0,0,0,.07);
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
body{
|
||||||
|
font-family:'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
|
||||||
|
background:var(--bg);color:var(--text);line-height:1.5;
|
||||||
|
}
|
||||||
|
.container{max-width:1100px;margin:0 auto;padding:0 16px}
|
||||||
|
a{text-decoration:none;color:inherit}
|
||||||
|
|
||||||
|
/* ===== Topbar ===== */
|
||||||
|
.topbar{background:var(--primary);color:#fff;position:sticky;top:0;z-index:50;box-shadow:var(--shadow)}
|
||||||
|
.topbar-inner{display:flex;align-items:center;gap:18px;padding:12px 16px}
|
||||||
|
.brand{display:flex;align-items:center;gap:8px;font-weight:800;font-size:20px}
|
||||||
|
.brand-logo{font-size:24px}
|
||||||
|
.brand-dot{color:var(--accent)}
|
||||||
|
.search-box{flex:1}
|
||||||
|
.search-box input{
|
||||||
|
width:100%;padding:10px 14px;border:none;border-radius:30px;font-size:14px;outline:none;
|
||||||
|
}
|
||||||
|
.cart-btn{
|
||||||
|
background:#fff;color:var(--primary);border:none;border-radius:30px;
|
||||||
|
padding:9px 16px;font-weight:700;cursor:pointer;font-size:14px;display:flex;align-items:center;gap:6px;
|
||||||
|
}
|
||||||
|
.cart-count{
|
||||||
|
background:var(--accent);color:#000;border-radius:50%;min-width:20px;height:20px;
|
||||||
|
display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Category nav ===== */
|
||||||
|
.catnav{background:#fff;border-bottom:1px solid var(--line)}
|
||||||
|
.catnav-inner{display:flex;gap:8px;overflow-x:auto;padding:10px 16px}
|
||||||
|
.cat-pill{
|
||||||
|
white-space:nowrap;padding:7px 14px;border-radius:30px;background:var(--bg);
|
||||||
|
border:1px solid var(--line);cursor:pointer;font-size:13px;font-weight:600;color:var(--muted);
|
||||||
|
}
|
||||||
|
.cat-pill.active{background:var(--primary);color:#fff;border-color:var(--primary)}
|
||||||
|
|
||||||
|
/* ===== Main / views ===== */
|
||||||
|
.main{padding:22px 16px 60px}
|
||||||
|
.view{display:none}
|
||||||
|
.view.active{display:block;animation:fade .25s ease}
|
||||||
|
@keyframes fade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
|
||||||
|
|
||||||
|
.hero{
|
||||||
|
background:linear-gradient(120deg,var(--primary),var(--accent));
|
||||||
|
color:#fff;border-radius:var(--radius);padding:28px;margin-bottom:22px;text-align:center;
|
||||||
|
}
|
||||||
|
.hero h1{font-size:26px;margin-bottom:6px}
|
||||||
|
.hero p{opacity:.95}
|
||||||
|
|
||||||
|
/* ===== Product grid ===== */
|
||||||
|
.product-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:16px}
|
||||||
|
.product-card{
|
||||||
|
background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);
|
||||||
|
overflow:hidden;display:flex;flex-direction:column;transition:transform .15s;
|
||||||
|
}
|
||||||
|
.product-card:hover{transform:translateY(-4px)}
|
||||||
|
.pc-thumb{
|
||||||
|
height:130px;display:flex;align-items:center;justify-content:center;font-size:60px;
|
||||||
|
background:linear-gradient(135deg,#fff3f3,#ffe9e9);
|
||||||
|
}
|
||||||
|
.pc-body{padding:12px;display:flex;flex-direction:column;gap:6px;flex:1}
|
||||||
|
.pc-cat{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.5px}
|
||||||
|
.pc-name{font-weight:700;font-size:15px;line-height:1.3}
|
||||||
|
.pc-meta{font-size:12px;color:var(--muted)}
|
||||||
|
.pc-badge{display:inline-block;background:var(--accent);color:#000;font-size:10px;font-weight:800;
|
||||||
|
padding:2px 7px;border-radius:20px;margin-left:6px}
|
||||||
|
.pc-rating{color:#f59e0b;font-size:12px}
|
||||||
|
.pc-price{font-size:17px;font-weight:800;color:var(--primary);margin-top:auto}
|
||||||
|
.pc-btn{
|
||||||
|
margin-top:8px;background:var(--primary);color:#fff;border:none;border-radius:8px;
|
||||||
|
padding:9px;font-weight:700;cursor:pointer;font-size:13px;
|
||||||
|
}
|
||||||
|
.pc-btn:hover{background:var(--primary-dark)}
|
||||||
|
.empty-note{text-align:center;color:var(--muted);padding:40px}
|
||||||
|
|
||||||
|
/* ===== Cart ===== */
|
||||||
|
.cart-list{display:flex;flex-direction:column;gap:12px}
|
||||||
|
.cart-row{
|
||||||
|
background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);
|
||||||
|
padding:14px;display:flex;align-items:center;gap:14px;
|
||||||
|
}
|
||||||
|
.cart-emoji{font-size:40px;width:60px;text-align:center}
|
||||||
|
.cart-info{flex:1}
|
||||||
|
.cart-info h4{font-size:15px}
|
||||||
|
.cart-info small{color:var(--muted)}
|
||||||
|
.qty{display:flex;align-items:center;gap:8px}
|
||||||
|
.qty button{width:28px;height:28px;border-radius:6px;border:1px solid var(--line);
|
||||||
|
background:#fff;cursor:pointer;font-size:16px;font-weight:700}
|
||||||
|
.qty span{min-width:24px;text-align:center;font-weight:700}
|
||||||
|
.cart-row .price{font-weight:800;color:var(--primary);min-width:110px;text-align:right}
|
||||||
|
.cart-row .rm{background:none;border:none;color:var(--muted);cursor:pointer;font-size:18px}
|
||||||
|
.cart-empty{text-align:center;padding:40px;background:#fff;border-radius:var(--radius);box-shadow:var(--shadow)}
|
||||||
|
.cart-summary{margin-top:18px;background:var(--card);border-radius:var(--radius);
|
||||||
|
box-shadow:var(--shadow);padding:16px;display:flex;justify-content:space-between;align-items:center}
|
||||||
|
.summary-row{display:flex;justify-content:space-between;padding:6px 0;font-size:14px}
|
||||||
|
.summary-row.total{font-size:18px;font-weight:800;color:var(--primary);border-top:1px solid var(--line);margin-top:6px;padding-top:10px}
|
||||||
|
|
||||||
|
/* ===== Buttons ===== */
|
||||||
|
.btn-primary{background:var(--primary);color:#fff;border:none;border-radius:8px;
|
||||||
|
padding:11px 22px;font-weight:700;cursor:pointer;font-size:14px}
|
||||||
|
.btn-primary:hover{background:var(--primary-dark)}
|
||||||
|
.btn-primary.full{width:100%;margin-top:12px}
|
||||||
|
|
||||||
|
/* ===== Checkout ===== */
|
||||||
|
.checkout-grid{display:grid;grid-template-columns:1fr 320px;gap:18px;align-items:start}
|
||||||
|
.card{background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);padding:18px;margin-bottom:18px}
|
||||||
|
.card h3{margin-bottom:14px;font-size:16px}
|
||||||
|
.card label{display:block;font-size:13px;font-weight:600;margin:12px 0 5px;color:var(--muted)}
|
||||||
|
.card input,.card textarea{
|
||||||
|
width:100%;padding:10px 12px;border:1px solid var(--line);border-radius:8px;font-size:14px;
|
||||||
|
font-family:inherit;outline:none;resize:vertical;
|
||||||
|
}
|
||||||
|
.card input:focus,.card textarea:focus{border-color:var(--primary)}
|
||||||
|
.opt-list{display:flex;flex-direction:column;gap:8px}
|
||||||
|
.opt{
|
||||||
|
display:flex;align-items:center;gap:10px;border:1px solid var(--line);border-radius:8px;
|
||||||
|
padding:11px 13px;cursor:pointer;font-size:14px;
|
||||||
|
}
|
||||||
|
.opt:hover{border-color:var(--primary)}
|
||||||
|
.opt.selected{border-color:var(--primary);background:#fff5f5}
|
||||||
|
.opt input{width:auto}
|
||||||
|
.opt small{color:var(--muted);margin-left:auto}
|
||||||
|
.summary-card{position:sticky;top:90px}
|
||||||
|
.order-items{display:flex;flex-direction:column;gap:8px;margin-bottom:10px}
|
||||||
|
.order-item{display:flex;justify-content:space-between;font-size:13px}
|
||||||
|
.order-item small{color:var(--muted)}
|
||||||
|
|
||||||
|
/* ===== Done ===== */
|
||||||
|
.done-box{background:var(--card);border-radius:var(--radius);box-shadow:var(--shadow);
|
||||||
|
padding:40px;text-align:center;max-width:520px;margin:30px auto}
|
||||||
|
.done-icon{font-size:64px}
|
||||||
|
.done-box h2{margin:10px 0}
|
||||||
|
.done-detail{text-align:left;background:var(--bg);border-radius:10px;padding:16px;margin:18px 0;font-size:14px}
|
||||||
|
.done-detail div{padding:4px 0}
|
||||||
|
|
||||||
|
/* ===== Footer ===== */
|
||||||
|
.footer{background:#fff;border-top:1px solid var(--line);padding:20px 0;text-align:center;color:var(--muted);font-size:13px}
|
||||||
|
|
||||||
|
/* ===== Toast ===== */
|
||||||
|
.toast{
|
||||||
|
position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(120px);
|
||||||
|
background:#1d1d1f;color:#fff;padding:12px 22px;border-radius:30px;font-size:14px;
|
||||||
|
font-weight:600;opacity:0;transition:.3s;z-index:100;box-shadow:var(--shadow);
|
||||||
|
}
|
||||||
|
.toast.show{transform:translateX(-50%) translateY(0);opacity:1}
|
||||||
|
|
||||||
|
/* ===== Responsive ===== */
|
||||||
|
@media(max-width:760px){
|
||||||
|
.checkout-grid{grid-template-columns:1fr}
|
||||||
|
.summary-card{position:static}
|
||||||
|
.topbar-inner{gap:10px}
|
||||||
|
.search-box{order:3;flex-basis:100%}
|
||||||
|
}
|
||||||
121
index.html
Normal file
121
index.html
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="id">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>OmahCitra Store — Toko Online Terlaris Indonesia</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ===== HEADER ===== -->
|
||||||
|
<header class="topbar">
|
||||||
|
<div class="container topbar-inner">
|
||||||
|
<a href="#" class="brand" data-view="catalog">
|
||||||
|
<span class="brand-logo">🛍️</span>
|
||||||
|
<span class="brand-name">OmahCitra<span class="brand-dot">Store</span></span>
|
||||||
|
</a>
|
||||||
|
<div class="search-box">
|
||||||
|
<input type="text" id="searchInput" placeholder="Cari produk terlaris...">
|
||||||
|
</div>
|
||||||
|
<button class="cart-btn" id="cartBtn">
|
||||||
|
🛒 Keranjang <span class="cart-count" id="cartCount">0</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- ===== CATEGORY NAV ===== -->
|
||||||
|
<nav class="catnav">
|
||||||
|
<div class="container catnav-inner" id="catNav"></div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main class="container main">
|
||||||
|
|
||||||
|
<!-- ===== VIEW: KATALOG ===== -->
|
||||||
|
<section id="view-catalog" class="view active">
|
||||||
|
<div class="hero">
|
||||||
|
<h1>Produk Paling Laku di Indonesia 🇮🇩</h1>
|
||||||
|
<p>Belanja koleksi terlaris — elektronik, fashion, makanan, & rumah tangga.</p>
|
||||||
|
</div>
|
||||||
|
<div class="product-grid" id="productGrid"></div>
|
||||||
|
<p class="empty-note" id="emptyNote" style="display:none">Produk tidak ditemukan.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== VIEW: KERANJANG ===== -->
|
||||||
|
<section id="view-cart" class="view">
|
||||||
|
<h2>🛒 Keranjang Belanja</h2>
|
||||||
|
<div id="cartItems" class="cart-list"></div>
|
||||||
|
<div id="cartEmpty" class="cart-empty" style="display:none">
|
||||||
|
<p>Keranjang kamu masih kosong.</p>
|
||||||
|
<button class="btn-primary" data-view="catalog">← Lanjut Belanja</button>
|
||||||
|
</div>
|
||||||
|
<div class="cart-summary" id="cartSummary" style="display:none">
|
||||||
|
<div class="summary-row"><span>Subtotal</span><span id="sumSubtotal">Rp0</span></div>
|
||||||
|
<button class="btn-primary" id="toCheckout">Lanjut ke Pembayaran →</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== VIEW: CHECKOUT (ORDER / PAYMENT / SHIPPING) ===== -->
|
||||||
|
<section id="view-checkout" class="view">
|
||||||
|
<h2>📦 Checkout</h2>
|
||||||
|
<div class="checkout-grid">
|
||||||
|
<div class="checkout-left">
|
||||||
|
<div class="card">
|
||||||
|
<h3>1. Data Penerima & Pengiriman</h3>
|
||||||
|
<label>Nama Lengkap</label>
|
||||||
|
<input type="text" id="custName" placeholder="Nama penerima">
|
||||||
|
<label>Alamat Lengkap</label>
|
||||||
|
<textarea id="custAddr" placeholder="Jalan, nomor, RT/RW, kelurahan, kecamatan, kota"></textarea>
|
||||||
|
<label>No. HP / WhatsApp</label>
|
||||||
|
<input type="text" id="custPhone" placeholder="08xxxxxxxxxx">
|
||||||
|
|
||||||
|
<label>Pilih Kurir</label>
|
||||||
|
<div id="courierOptions" class="opt-list"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h3>2. Metode Pembayaran</h3>
|
||||||
|
<div id="paymentOptions" class="opt-list"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<aside class="checkout-right">
|
||||||
|
<div class="card summary-card">
|
||||||
|
<h3>Ringkasan Order</h3>
|
||||||
|
<div id="orderItems" class="order-items"></div>
|
||||||
|
<div class="summary-row"><span>Subtotal</span><span id="coSubtotal">Rp0</span></div>
|
||||||
|
<div class="summary-row"><span>Ongkos Kirim</span><span id="coShipping">Rp0</span></div>
|
||||||
|
<div class="summary-row total"><span>Total Bayar</span><span id="coTotal">Rp0</span></div>
|
||||||
|
<button class="btn-primary full" id="placeOrder">Buat Pesanan</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== VIEW: KONFIRMASI ORDER ===== -->
|
||||||
|
<section id="view-done" class="view">
|
||||||
|
<div class="done-box">
|
||||||
|
<div class="done-icon">✅</div>
|
||||||
|
<h2>Pesanan Berhasil Dibuat!</h2>
|
||||||
|
<p>No. Pesanan: <strong id="orderNo"></strong></p>
|
||||||
|
<div id="doneDetail" class="done-detail"></div>
|
||||||
|
<button class="btn-primary" data-view="catalog">Belanja Lagi</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<p>© 2026 OmahCitra Store — Demo toko online. Dibangun dengan HTML, CSS & jQuery.</p>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Toast -->
|
||||||
|
<div class="toast" id="toast"></div>
|
||||||
|
|
||||||
|
<script src="js/jquery-3.7.1.min.js"></script>
|
||||||
|
<script src="js/products.js"></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
250
js/app.js
Normal file
250
js/app.js
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
/* ============================================================
|
||||||
|
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();
|
||||||
|
});
|
||||||
2
js/jquery-3.7.1.min.js
vendored
Normal file
2
js/jquery-3.7.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
163
js/products.js
Normal file
163
js/products.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/* ============================================================
|
||||||
|
OmahCitra Store - Daftar produk paling laku di Indonesia
|
||||||
|
Data statis (simulasi) - diurutkan berdasar kategori terlaris
|
||||||
|
============================================================ */
|
||||||
|
const PRODUCTS = [
|
||||||
|
// --- Elektronik & Smartphone (kategori paling laku) ---
|
||||||
|
{
|
||||||
|
id: "p01",
|
||||||
|
name: "Samsung Galaxy A55 5G",
|
||||||
|
category: "Elektronik",
|
||||||
|
price: 4299000,
|
||||||
|
rating: 4.7,
|
||||||
|
sold: 12400,
|
||||||
|
badge: "Terlaris",
|
||||||
|
emoji: "📱",
|
||||||
|
desc: "HP 5G RAM 8GB/256GB, kamera 50MP, layar Super AMOLED 120Hz."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p02",
|
||||||
|
name: "Xiaomi Redmi Note 13 Pro",
|
||||||
|
category: "Elektronik",
|
||||||
|
price: 3299000,
|
||||||
|
rating: 4.6,
|
||||||
|
sold: 9820,
|
||||||
|
badge: "Populer",
|
||||||
|
emoji: "📱",
|
||||||
|
desc: "Snapdragon 7s Gen 2, kamera 200MP, baterai 5000mAh fast charge."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p03",
|
||||||
|
name: "Apple AirPods Gen 2",
|
||||||
|
category: "Elektronik",
|
||||||
|
price: 1499000,
|
||||||
|
rating: 4.8,
|
||||||
|
sold: 15600,
|
||||||
|
badge: "Terlaris",
|
||||||
|
emoji: "🎧",
|
||||||
|
desc: "Earbuds nirkabel dengan chip H1, audio berkualitas & low latency."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p04",
|
||||||
|
name: "Charger Adaptor 33W Fast Charge",
|
||||||
|
category: "Elektronik",
|
||||||
|
price: 129000,
|
||||||
|
rating: 4.5,
|
||||||
|
sold: 22100,
|
||||||
|
badge: "",
|
||||||
|
emoji: "🔌",
|
||||||
|
desc: "Adaptor GAN cepat universal, kompatibel banyak merek HP."
|
||||||
|
},
|
||||||
|
|
||||||
|
// --- Fashion & Kecantikan ---
|
||||||
|
{
|
||||||
|
id: "p05",
|
||||||
|
name: "Hoodie Unisex Oversize",
|
||||||
|
category: "Fashion",
|
||||||
|
price: 189000,
|
||||||
|
rating: 4.4,
|
||||||
|
sold: 18900,
|
||||||
|
badge: "Trending",
|
||||||
|
emoji: "👕",
|
||||||
|
desc: "Hoodie katun tebal nyaman, cocok pria & wanita, banyak warna."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p06",
|
||||||
|
name: "Skincare Serum Vitamin C",
|
||||||
|
category: "Kecantikan",
|
||||||
|
price: 95000,
|
||||||
|
rating: 4.6,
|
||||||
|
sold: 27300,
|
||||||
|
badge: "Terlaris",
|
||||||
|
emoji: "🧴",
|
||||||
|
desc: "Serum wajah brightening 30ml, cerahkan & haluskan kulit."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p07",
|
||||||
|
name: "Sneakers Casual Pria",
|
||||||
|
category: "Fashion",
|
||||||
|
price: 259000,
|
||||||
|
rating: 4.3,
|
||||||
|
sold: 11200,
|
||||||
|
badge: "",
|
||||||
|
emoji: "👟",
|
||||||
|
desc: "Sepatu sneakers ringan anti-slip, sol empuk untuk daily use."
|
||||||
|
},
|
||||||
|
|
||||||
|
// --- Makanan & Minuman (FMCG paling laku) ---
|
||||||
|
{
|
||||||
|
id: "p08",
|
||||||
|
name: "Indomie Goreng 40 Pcs",
|
||||||
|
category: "Makanan",
|
||||||
|
price: 138000,
|
||||||
|
rating: 4.9,
|
||||||
|
sold: 41200,
|
||||||
|
badge: "Terlaris",
|
||||||
|
emoji: "🍜",
|
||||||
|
desc: "Mi instan goreng kemasan ekonomis, rasa favorit Indonesia."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p09",
|
||||||
|
name: "Kopi Susu ABC 10 Sachet",
|
||||||
|
category: "Makanan",
|
||||||
|
price: 45000,
|
||||||
|
rating: 4.7,
|
||||||
|
sold: 33800,
|
||||||
|
badge: "",
|
||||||
|
emoji: "☕",
|
||||||
|
desc: "Kopi susu kemasan sachet, praktis & nikmat untuk sahur/ngopi."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p10",
|
||||||
|
name: "Teh Botol Sosro 350ml (12 botol)",
|
||||||
|
category: "Makanan",
|
||||||
|
price: 72000,
|
||||||
|
rating: 4.8,
|
||||||
|
sold: 29500,
|
||||||
|
badge: "Populer",
|
||||||
|
emoji: "🍵",
|
||||||
|
desc: "Teh hijau siap minum original, segar dan tanpa pengawet berlebih."
|
||||||
|
},
|
||||||
|
|
||||||
|
// --- Rumah Tangga ---
|
||||||
|
{
|
||||||
|
id: "p11",
|
||||||
|
name: "Rice Cooker 1.8L Anti Lengket",
|
||||||
|
category: "Rumah Tangga",
|
||||||
|
price: 265000,
|
||||||
|
rating: 4.5,
|
||||||
|
sold: 14300,
|
||||||
|
badge: "",
|
||||||
|
emoji: "🍚",
|
||||||
|
desc: "Magic com hemat listrik, memasak otomatis & keep warm."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "p12",
|
||||||
|
name: "Deterjen Bubuk 3kg",
|
||||||
|
category: "Rumah Tangga",
|
||||||
|
price: 58000,
|
||||||
|
rating: 4.6,
|
||||||
|
sold: 26700,
|
||||||
|
badge: "Populer",
|
||||||
|
emoji: "🧼",
|
||||||
|
desc: "Deterjen wangi tahan lama, cocok mesin cuci & cuci tangan."
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
// Opsi kurir / pengiriman
|
||||||
|
const COURIERS = [
|
||||||
|
{ id: "jne", name: "JNE Regular", fee: 15000, etd: "2-3 hari" },
|
||||||
|
{ id: "sicepat", name: "SiCepat BEST", fee: 18000, etd: "1-2 hari" },
|
||||||
|
{ id: "pos", name: "POS Indonesia", fee: 12000, etd: "3-5 hari" },
|
||||||
|
{ id: "gosend", name: "GoSend Same Day", fee: 35000, etd: "Hari ini" }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Opsi metode pembayaran
|
||||||
|
const PAYMENTS = [
|
||||||
|
{ id: "qris", name: "QRIS (All e-wallet & m-banking)" },
|
||||||
|
{ id: "gopay", name: "GoPay" },
|
||||||
|
{ id: "ovo", name: "OVO" },
|
||||||
|
{ id: "dana", name: "DANA" },
|
||||||
|
{ id: "transfer", name: "Transfer Bank (BCA / Mandiri / BRI)" },
|
||||||
|
{ id: "cod", name: "Bayar di Tempat (COD)" }
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user