0% found this document useful (0 votes)
18 views21 pages

Mesin HTML

The document is an HTML template for a menu machine application featuring a smooth checkmark animation. It includes styles for various components such as buttons, sliders, lists, and modals, with a focus on a dark theme and responsive design. The layout is structured to facilitate user interaction with menu items, cart functionalities, and payment options.

Uploaded by

branskyzz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
18 views21 pages

Mesin HTML

The document is an HTML template for a menu machine application featuring a smooth checkmark animation. It includes styles for various components such as buttons, sliders, lists, and modals, with a focus on a dark theme and responsive design. The layout is structured to facilitate user interaction with menu items, cart functionalities, and payment options.

Uploaded by

branskyzz
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

<!

DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Menu Machine - Rupiah (Smooth Checkmark Animation)</title>
<style>
@import url('https://fonts.googleapis.com/css2?
family=Montserrat:wght@400;600&display=swap');
/* Reset & base */
* {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
body {
margin: 0;
font-family: 'Montserrat', sans-serif;
background: #222222;
color: #e5e1d8;
display: flex;
justify-content: center;
padding: 30px 20px;
min-height: 100vh;
user-select: none;
-webkit-user-select: none;
overflow-x: hidden;
}
h1 {
font-weight: 600;
font-size: 2rem;
text-align: center;
margin-bottom: 15px;
color: #c9ad6a;
letter-spacing: 0.08em;
text-shadow:
0 1px 2px rgba(0,0,0,0.5);
}

/* Container */
.machine-container {
background: #2f2f2f;
border-radius: 20px;
box-shadow:
inset 0 0 10px rgba(255, 238, 160, 0.25),
0 8px 20px rgba(0,0,0,0.7);
max-width: 450px;
width: 100%;
padding: 30px 28px 36px 28px;
display: flex;
flex-direction: column;
gap: 18px;
user-select: none;
-webkit-user-select: none;
}

/* Category slider container */


.category-slider-container {
position: relative;
width: 100%;
overflow: hidden;
margin-bottom: 20px;
user-select: none;
}
.category-slider {
display: flex;
gap: 14px;
transition: transform 0.35s ease;
will-change: transform;
padding-bottom: 4px;
user-select: none;
}
.cat-slide-btn {
background: transparent;
border: 2px solid #b89f3b;
padding: 10px 26px;
border-radius: 30px;
color: #c9ad6a;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
flex-shrink: 0;
box-shadow: inset 0 0 4px rgba(184,159,59,0.2);
transition:
background 0.3s ease,
color 0.3s ease,
box-shadow 0.3s ease;
user-select: none;
}
.cat-slide-btn:hover {
background: #b89f3b;
color: #222222;
box-shadow: 0 0 8px #b89f3b, inset 0 0 6px #f0de76;
}
.cat-slide-btn.active {
background: #b89f3b;
box-shadow: 0 0 10px #b89f3b, inset 0 0 8px #f0de76;
color: #222222;
cursor: default;
font-weight: 700;
user-select: none;
}

/* Arrow navigation buttons */


.slider-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 36px;
height: 36px;
background: #c9ad6a;
border-radius: 50%;
box-shadow: 0 4px 10px rgba(140,116,44,0.7);
border: none;
color: #2f2f2f;
font-weight: 700;
font-size: 24px;
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
justify-content: center;
transition:
background 0.3s ease,
transform 0.15s ease,
box-shadow 0.3s ease;
z-index: 10;
}
.slider-nav:hover {
background: #dbce7d;
box-shadow: 0 8px 24px rgba(219, 206, 125, 0.9);
transform: translateY(-50%) scale(1.1);
}
.slider-nav:active {
transform: translateY(-50%) scale(0.9);
box-shadow: 0 3px 8px rgba(140,116,44,0.5);
}
.slider-nav.left {
left: -46px;
}
.slider-nav.right {
right: -46px;
}

/* Menu list */
.menu-list {
list-style: none;
padding: 0;
margin: 0;
max-height: 280px;
overflow-y: auto;
border-radius: 14px;
background: #363636;
box-shadow:
inset 0 0 15px rgba(255, 238, 160, 0.15);
}
.menu-item {
display: flex;
justify-content: flex-start;
align-items: center;
gap: 14px;
padding: 12px 20px;
border-bottom: 1px solid rgba(184,159,59,0.18);
font-weight: 600;
font-size: 1.05rem;
color: #f7f3d7;
cursor: default;
transition: background 0.3s ease;
}
.menu-item:last-child {
border-bottom: none;
}
.menu-item:hover {
background: rgba(184,159,59,0.15);
}
.menu-item img {
width: 64px;
height: 64px;
object-fit: cover;
border-radius: 14px;
box-shadow: 0 3px 8px rgba(201, 173, 106, 0.8);
flex-shrink: 0;
user-select: none;
}
.item-info {
display: flex;
flex-direction: column;
justify-content: center;
flex-grow: 1;
min-width: 80px;
}
.item-name {
font-size: 1.1rem;
margin-bottom: 4px;
color: #f3e9bb;
user-select: text;
}
.item-price {
color: #d4c76d;
font-weight: 700;
font-family: 'Montserrat', sans-serif;
user-select: text;
}
.add-btn {
background: #c9ad6a;
border: none;
color: #2f2f2f;
font-weight: 700;
padding: 7px 22px;
border-radius: 28px;
box-shadow:
0 3px 0 #8e7c34;
cursor: pointer;
font-size: 0.9rem;
transition:
background 0.3s ease,
transform 0.15s ease,
box-shadow 0.3s ease;
user-select: none;
flex-shrink: 0;
white-space: nowrap;
}
.add-btn:hover {
background: #e1d78e;
box-shadow:
0 4px 0 #8e7c34;
transform: translateY(-2px);
}
.add-btn:active {
box-shadow:
0 1px 0 #8e7c34;
transform: translateY(1px);
}

/* Cart */
.cart-section {
background: #363636;
border-radius: 18px;
padding: 16px 24px 22px 24px;
box-shadow:
inset 0 0 20px rgba(255, 238, 160, 0.1);
font-weight: 600;
color: #f5f1cc;
display: flex;
flex-direction: column;
gap: 14px;
user-select: none;
}
.cart-title {
font-weight: 700;
font-size: 1.3rem;
color: #c9ad6a;
text-align: center;
letter-spacing: 0.08em;
text-shadow:
0 0 5px rgba(201, 173, 106, 0.8);
}
.cart-list {
list-style: none;
padding: 0;
margin: 0;
max-height: 160px;
overflow-y: auto;
border-radius: 12px;
background: #2f2f2f;
box-shadow: inset 0 0 10px rgba(201, 173, 106, 0.15);
}
.cart-item {
display: flex;
justify-content: space-between;
padding: 10px 15px;
border-bottom: 1px solid rgba(184,159,59,0.12);
font-size: 1rem;
color: #efe9c7;
font-weight: 500;
}
.cart-item:last-child {
border-bottom: none;
}
.cart-item-name {
flex-grow: 1;
}
.cart-item-qty {
margin: 0 14px;
color: #bcaa51;
}
.cart-item-price {
min-width: 85px;
text-align: right;
font-weight: 700;
color: #c9ad6a;
font-family: 'Montserrat', sans-serif;
}
.cart-total {
display: flex;
justify-content: space-between;
font-size: 1.35rem;
font-weight: 800;
color: #c9ad6a;
font-family: 'Montserrat', sans-serif;
padding: 10px 0 6px 0;
border-top: 2px solid #8e7c34;
text-shadow: 0 0 6px #8e7c34;
user-select: text;
}

/* Payment */
.payment-section {
display: flex;
gap: 14px;
flex-wrap: wrap;
justify-content: center;
user-select: none;
}
.payment-input {
flex-grow: 1;
min-width: 180px;
max-width: 220px;
padding: 14px 16px;
border-radius: 18px;
border: 2.5px solid #c9ad6a;
background: #2f2f2f;
color: #f3ecb2;
font-size: 1.1rem;
font-weight: 600;
font-family: 'Montserrat', sans-serif;
text-align: right;
box-shadow:
inset 0 5px 8px rgba(201, 173, 106, 0.35);
transition: border-color 0.3s ease;
outline: none;
user-select: text;
}
.payment-input::placeholder {
color: #d2c67acc;
font-weight: 500;
font-family: 'Montserrat', sans-serif;
text-align: left;
}
.payment-input:focus {
border-color: #e1d78e;
box-shadow:
inset 0 6px 15px rgba(225, 215, 142, 0.6);
}
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.pay-btn {
background: #c9ad6a;
border: none;
color: #2f2f2f;
padding: 14px 36px;
border-radius: 28px;
font-weight: 700;
font-family: 'Montserrat', sans-serif;
font-size: 1.05rem;
cursor: pointer;
box-shadow:
0 6px 15px rgba(140,116,44,0.8);
text-transform: uppercase;
transition:
background 0.3s ease,
transform 0.15s ease,
box-shadow 0.3s ease;
user-select: none;
}
.pay-btn:hover {
background: #dbce7d;
box-shadow:
0 10px 25px rgba(219, 206, 125, 1);
transform: translateY(-3px);
}
.pay-btn:active {
transform: translateY(2px);
box-shadow:
0 3px 8px rgba(140,116,44,0.5);
}

/* Messages */
.message {
font-weight: 600;
font-size: 1.05rem;
min-height: 28px;
text-align: center;
user-select: none;
letter-spacing: 0.04em;
}
.message.error {
color: #d96767;
text-shadow:
0 0 6px #d96767dd;
}
.message.success {
color: #c9ad6a;
text-shadow:
0 0 10px #c9ad6a88;
}

/* Scrollbar */
.menu-list::-webkit-scrollbar,
.cart-list::-webkit-scrollbar {
width: 8px;
}
.menu-list::-webkit-scrollbar-track,
.cart-list::-webkit-scrollbar-track {
background: #2f2f2f;
border-radius: 10px;
}
.menu-list::-webkit-scrollbar-thumb,
.cart-list::-webkit-scrollbar-thumb {
background: #c9ad6aaa;
border-radius: 10px;
border: 2px solid #2f2f2f;
}
.menu-list::-webkit-scrollbar-thumb:hover,
.cart-list::-webkit-scrollbar-thumb:hover {
background: #c9ad6add;
}

/* Responsive */
@media (max-width: 460px) {
.machine-container {
width: 100%;
padding: 24px 20px 32px 20px;
}
.payment-section {
flex-direction: column;
gap: 12px;
}
.payment-input {
max-width: 100%;
text-align: center;
font-size: 1.2rem;
}
.pay-btn {
width: 100%;
}
.category-slider-container {
margin-bottom: 16px;
}
.cat-slide-btn {
padding: 7px 18px;
font-size: 0.9rem;
}
.slider-nav.left {
left: 10px;
}
.slider-nav.right {
right: 10px;
}
}

/* Modal overlay */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.77);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.modal-overlay.active {
opacity: 1;
pointer-events: all;
}

/* Modal content */
.modal {
background: #2f2f2f;
border-radius: 20px;
box-shadow:
0 4px 30px rgba(201, 173, 106, 0.85);
width: 360px;
max-width: 90vw;
padding: 24px 28px 30px 28px;
color: #f5f1cc;
display: flex;
flex-direction: column;
gap: 20px;
user-select: none;
position: relative;
font-family: 'Montserrat', sans-serif;
}
.modal h2 {
font-size: 1.6rem;
font-weight: 700;
text-align: center;
color: #c9ad6a;
text-shadow:
0 0 12px #c9ad6a;
user-select: text;
}
.modal-message {
font-size: 1.1rem;
font-weight: 600;
text-align: center;
min-height: 60px;
color: #efe9c7;
white-space: pre-wrap;
user-select: text;
}

/* Payment options */
.payment-methods {
display: flex;
flex-direction: column;
gap: 14px;
margin-top: 10px;
user-select: none;
}
.payment-method {
background: #363636;
border-radius: 14px;
padding: 14px 18px;
box-shadow: inset 0 0 12px rgba(201, 173, 106, 0.15);
cursor: pointer;
transition: background 0.25s ease;
font-weight: 600;
color: #f3ecb2;
display: flex;
align-items: center;
gap: 12px;
border: 2px solid transparent;
user-select: none;
border-left: 6px solid transparent;
}
.payment-method:hover {
background: #4a4a4a;
border-left-color: #c9ad6a;
}
.payment-method.selected {
background: #b89f3b;
border-left-color: #7a6715;
color: #222222;
font-weight: 700;
}
.payment-method img {
width: 28px;
height: 28px;
object-fit: contain;
user-select: none;
pointer-events: none;
}

/* Instructions inside modal */


.payment-instructions {
font-size: 1rem;
font-weight: 500;
color: #efefdf;
background: #1f1f1f;
border-radius: 12px;
padding: 15px;
box-shadow: inset 0 0 8px rgba(201, 173, 106, 0.3);
min-height: 90px;
user-select: text;
white-space: pre-wrap;
}

/* Modal buttons */
.modal-buttons {
display: flex;
justify-content: flex-end;
gap: 12px;
margin-top: 6px;
}
.modal-btn {
cursor: pointer;
background: #c9ad6a;
border: none;
padding: 10px 28px;
border-radius: 28px;
font-weight: 700;
font-family: 'Montserrat', sans-serif;
font-size: 1rem;
color: #2f2f2f;
box-shadow:
0 6px 15px rgba(140,116,44,0.8);
transition:
background 0.3s ease,
transform 0.15s ease,
box-shadow 0.3s ease;
user-select: none;
}
.modal-btn:hover {
background: #dbce7d;
box-shadow:
0 10px 25px rgba(219, 206, 125, 1);
transform: translateY(-3px);
}
.modal-btn:active {
transform: translateY(2px);
box-shadow:
0 3px 8px rgba(140,116,44,0.5);
}

/* Checkmark animation container */


.checkmark-container {
display: flex;
justify-content: center;
margin: 16px 0 0 0;
opacity: 0;
transform: scale(0.5);
pointer-events: none;
transition: opacity 0.6s ease, transform 0.6s cubic-bezier(0.4, 0, 0.2, 1);
}
.checkmark-container.show {
opacity: 1;
transform: scale(1);
pointer-events: auto;
}

/* Animated checkmark */
.checkmark-svg {
width: 90px;
height: 90px;
stroke-width: 8;
stroke: #c9ad6a;
stroke-miterlimit: 10;
fill: none;
filter: drop-shadow(0 0 8px #c9ad6a99);
}
.checkmark-circle {
stroke-dasharray: 160;
stroke-dashoffset: 160;
animation: circleDraw 0.8s forwards ease-out;
}
.checkmark-path {
stroke-dasharray: 62;
stroke-dashoffset: 62;
animation: pathDraw 0.7s 0.75s forwards ease-out;
}
@keyframes circleDraw {
to {
stroke-dashoffset: 0;
}
}
@keyframes pathDraw {
to {
stroke-dashoffset: 0;
}
}

/* Responsive */
@media (max-width: 480px) {
.modal {
width: 95vw;
padding: 20px 24px 26px 24px;
}
.payment-methods {
gap: 12px;
}
.modal-buttons {
justify-content: center;
}
.modal-btn {
width: 100%;
max-width: 320px;
padding: 14px 0;
}
.checkmark-svg {
width: 70px;
height: 70px;
}
}
</style>
</head>
<body>
<main class="machine-container" role="main" aria-label="Menu machine with Rupiah
pricing and categories with images">
<h1>MENU MACHINE</h1>

<section class="category-slider-container" aria-label="Navigasi kategori menu">


<button class="slider-nav left" aria-label="Kategori sebelumnya" id="nav-
left" tabindex="0">&#9664;</button>
<div class="category-slider" id="category-slider" role="tablist">
<!-- Category buttons inserted here by JS -->
</div>
<button class="slider-nav right" aria-label="Kategori berikutnya" id="nav-
right" tabindex="0">&#9654;</button>
</section>

<ul class="menu-list" id="item-list" aria-live="polite" aria-atomic="true"


tabindex="0" aria-label="Daftar menu kategori terpilih">
<!-- Menu items rendered by JS -->
</ul>

<section class="cart-section" aria-live="polite" aria-atomic="true">


<div class="cart-title">Keranjang Belanja</div>
<ul class="cart-list" id="cart-list">
<li>Keranjang kosong</li>
</ul>
<div class="cart-total">
<span>Total</span>
<span id="total-price">Rp 0</span>
</div>
<div class="payment-section">
<input
type="number"
id="payment-input"
placeholder="Masukkan uang Anda (Rp)"
aria-label="Input jumlah uang"
min="0"
class="payment-input"
/>
<button id="pay-btn" class="pay-btn" aria-label="Bayar
pesanan">Bayar</button>
</div>
<div class="message" id="message" role="alert" aria-live="assertive"></div>
</section>
</main>

<!-- Modal -->


<div class="modal-overlay" id="modal-overlay" role="dialog" aria-modal="true"
aria-labelledby="modal-title" aria-describedby="modal-desc" hidden>
<div class="modal" role="document">
<h2 id="modal-title">Pembayaran</h2>
<div class="modal-message" id="modal-desc"></div>
<div id="payment-options-container" class="payment-methods" hidden>
<button class="payment-method" data-method="cash" aria-label="Pembayaran
dengan tunai">
<img src="https://img.icons8.com/ios-filled/50/000000/money--v1.png"
alt="" aria-hidden="true" />
Tunai
</button>
<button class="payment-method" data-method="qr" aria-label="Pembayaran
dengan QR Code">
<img src="https://img.icons8.com/ios-filled/50/000000/qr-code.png" alt=""
aria-hidden="true" />
QR Payment
</button>
<button class="payment-method" data-method="m-banking" aria-
label="Pembayaran dengan M-Banking">
<img src="https://img.icons8.com/ios-filled/50/000000/mobile-payment.png"
alt="" aria-hidden="true" />
M-Banking
</button>
</div>
<div id="payment-instructions" class="payment-instructions" hidden></div>
<div class="checkmark-container" id="checkmark-container" aria-hidden="true">
<svg viewBox="0 0 52 52" class="checkmark-svg" aria-hidden="true"
focusable="false">
<circle class="checkmark-circle" cx="26" cy="26" r="25" fill="none"
stroke="#c9ad6a" stroke-width="4" />
<path class="checkmark-path" fill="none" stroke="#c9ad6a" stroke-
width="5" d="M14 27l7 7 16-16"/>
</svg>
</div>
<div class="modal-buttons" id="modal-buttons">
<button class="modal-btn" id="modal-cancel-btn" aria-label="Batal
pembayaran">Batal</button>
<button class="modal-btn" id="modal-confirm-btn" aria-label="Konfirmasi
pembayaran">Konfirmasi</button>
</div>
</div>
</div>

<script>
const categories = [
{ name: 'Minuman' },
{ name: 'Makanan' },
{ name: 'Lainnya' }
];
const menuItems = [
{ id: 1, name: 'Kopi Tubruk', category: 'Minuman', price: 8000, img:
'https://images.unsplash.com/photo-1509042239860-f550ce710b93?
auto=format&fit=crop&w=80&q=80' },
{ id: 2, name: 'Es Teh Manis', category: 'Minuman', price: 7000, img:
'https://images.unsplash.com/photo-1504674900247-0877df9cc836?
auto=format&fit=crop&w=80&q=80' },
{ id: 3, name: 'Jus Jeruk', category: 'Minuman', price: 12000, img:
'https://images.unsplash.com/photo-1551024601-bec78faa8d4b?
auto=format&fit=crop&w=80&q=80' },
{ id: 4, name: 'Roti Bakar', category: 'Makanan', price: 15000, img:
'https://images.unsplash.com/photo-1495195134817-aeb325a55b65?
auto=format&fit=crop&w=80&q=80' },
{ id: 5, name: 'Nasi Goreng', category: 'Makanan', price: 25000, img:
'https://images.unsplash.com/photo-1600891964599-f61ba0e24092?
auto=format&fit=crop&w=80&q=80' },
{ id: 6, name: 'Sate Ayam', category: 'Makanan', price: 30000, img:
'https://images.unsplash.com/photo-1576402187876-a8cb7132b226?
auto=format&fit=crop&w=80&q=80' },
{ id: 7, name: 'Telur Rebus', category: 'Lainnya', price: 5000, img:
'https://images.unsplash.com/photo-1604697964405-93ef0ab78bd6?
auto=format&fit=crop&w=80&q=80' },
{ id: 8, name: 'Keripik Singkong', category: 'Lainnya', price: 8000, img:
'https://images.unsplash.com/photo-1617191511614-ddd2b3efa3df?
auto=format&fit=crop&w=80&q=80' }
];

const categorySlider = document.getElementById('category-slider');


const itemListEl = document.getElementById('item-list');
const cartListEl = document.getElementById('cart-list');
const totalPriceEl = document.getElementById('total-price');
const paymentInput = document.getElementById('payment-input');
const payBtn = document.getElementById('pay-btn');
const messageEl = document.getElementById('message');
const navLeft = document.getElementById('nav-left');
const navRight = document.getElementById('nav-right');

// Modal elements
const modalOverlay = document.getElementById('modal-overlay');
const modalTitle = modalOverlay.querySelector('h2#modal-title');
const modalDesc = document.getElementById('modal-desc');
const paymentOptionsContainer = document.getElementById('payment-options-
container');
const paymentInstructions = document.getElementById('payment-instructions');
const modalButtons = document.getElementById('modal-buttons');
const modalCancelBtn = document.getElementById('modal-cancel-btn');
const modalConfirmBtn = document.getElementById('modal-confirm-btn');
const checkmarkContainer = document.getElementById('checkmark-container');

let cart = {};


let currentCategoryIndex = 0;
let modalPaymentSelected = null;
let paymentProcessing = false;
let totalDue = 0;
let paymentEntered = 0;

function formatRupiah(number) {
return 'Rp ' + number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
}

function renderCategorySlider() {
categorySlider.innerHTML = '';
categories.forEach((cat, i) => {
const btn = document.createElement('button');
btn.className = 'cat-slide-btn';
btn.textContent = cat.name;
btn.setAttribute('role', 'tab');
btn.setAttribute('aria-selected', i === currentCategoryIndex ? 'true' :
'false');
btn.id = `cat-btn-${i}`;
btn.tabIndex = i === currentCategoryIndex ? 0 : -1;
if(i === currentCategoryIndex) btn.classList.add('active');

btn.addEventListener('click', () => {
if(i === currentCategoryIndex) return;
setCurrentCategory(i);
});

categorySlider.appendChild(btn);
});
updateSliderPosition();
}

function updateSliderPosition() {
const btnWidth = categorySlider.children[0]?.offsetWidth || 140;
const gap = 14;
const totalWidth = btnWidth + gap;
const containerWidth = categorySlider.parentElement.offsetWidth;
const centerOffset = (containerWidth / 2) - (btnWidth / 2);
let translateX = -currentCategoryIndex * totalWidth + centerOffset;
const maxTranslate = 0;
const minTranslate = containerWidth - categorySlider.scrollWidth - 4;
if(translateX > maxTranslate) translateX = maxTranslate;
if(translateX < minTranslate) translateX = minTranslate;
categorySlider.style.transform = `translateX(${translateX}px)`;
}

function setCurrentCategory(index) {
if(index < 0) index = categories.length - 1;
else if(index >= categories.length) index = 0;
currentCategoryIndex = index;

Array.from(categorySlider.children).forEach((btn, i) => {
btn.classList.toggle('active', i === currentCategoryIndex);
btn.setAttribute('aria-selected', i === currentCategoryIndex ? 'true' :
'false');
btn.tabIndex = i === currentCategoryIndex ? 0 : -1;
});

updateSliderPosition();
renderMenu();
}

function renderMenu() {
const categoryName = categories[currentCategoryIndex].name;
const filtered = menuItems.filter(i => i.category === categoryName);
itemListEl.innerHTML = '';
if(filtered.length === 0){
itemListEl.innerHTML = `<li style="padding:16px; text-align:center;
color:#b5a755;">Tidak ada menu pada kategori ini.</li>`;
return;
}
filtered.forEach(item => {
const li = document.createElement('li');
li.className = 'menu-item';
li.innerHTML = `
<img src="${item.img}" alt="${item.name}" loading="lazy" />
<div class="item-info">
<span class="item-name">${item.name}</span>
<span class="item-price">${formatRupiah(item.price)}</span>
</div>
<button class="add-btn" data-id="${item.id}" aria-label="Tambah $
{item.name} ke keranjang">Tambah</button>
`;
itemListEl.appendChild(li);
});
document.querySelectorAll('.add-btn').forEach(btn => {
btn.addEventListener('click', () => {
const id = parseInt(btn.getAttribute('data-id'));
addToCart(id);
paymentInput.focus();
messageEl.textContent = '';
messageEl.className = 'message';
});
});
}

function renderCart() {
cartListEl.innerHTML = '';
const entries = Object.entries(cart);
if (entries.length === 0) {
cartListEl.innerHTML = '<li>Keranjang kosong</li>';
totalPriceEl.textContent = formatRupiah(0);
return;
}
let total = 0;
entries.forEach(([id, qty]) => {
const item = menuItems.find(i => i.id === parseInt(id));
const priceTotal = item.price * qty;
total += priceTotal;
const li = document.createElement('li');
li.className = 'cart-item';
li.innerHTML = `
<span class="cart-item-name">${item.name}</span>
<span class="cart-item-qty">x${qty}</span>
<span class="cart-item-price">${formatRupiah(priceTotal)}</span>
`;
cartListEl.appendChild(li);
});
totalPriceEl.textContent = formatRupiah(total);
}

function addToCart(id) {
if (!cart[id]) cart[id] = 0;
cart[id]++;
renderCart();
}

function getTotalPrice() {
return Object.entries(cart).reduce((acc, [id, qty]) => {
const item = menuItems.find(i => i.id === parseInt(id));
return acc + (item.price * qty);
}, 0);
}

function resetModalState() {
modalPaymentSelected = null;
paymentProcessing = false;
modalConfirmBtn.disabled = false;
modalCancelBtn.disabled = false;
paymentOptionsContainer.hidden = false;
paymentInstructions.hidden = true;
checkmarkContainer.classList.remove('show');
modalDesc.textContent = '';
clearPaymentSelection();
}

function clearPaymentSelection() {
paymentOptionsContainer.querySelectorAll('.payment-method').forEach(btn => {
btn.classList.remove('selected');
});
paymentInstructions.textContent = '';
paymentInstructions.hidden = true;
}

function showPaymentInstructions(method) {
let instructions = '';
switch(method) {
case 'cash':
instructions = 'Silakan masukkan uang tunai ke mesin setelah menekan
tombol "Konfirmasi".';
break;
case 'qr':
instructions = 'Scan QR Payment berikut ini dengan aplikasi pembayaran
Anda.\n\n[QR CODE SIMULASI]\n\nTunggu konfirmasi otomatis setelah transfer
berhasil.';
break;
case 'm-banking':
instructions = 'Gunakan aplikasi m-banking untuk mentransfer ke nomor
rekening berikut:\n123-456-7890.\n\nTunggu konfirmasi otomatis setelah transfer
berhasil.';
break;
default:
instructions = '';
}
paymentInstructions.textContent = instructions;
paymentInstructions.hidden = false;
}

function showModal(title, message, success = true) {


modalOverlay.hidden = false;
modalOverlay.classList.add('active');
modalTitle.textContent = title;
modalDesc.style.color = success ? '#c9ad6a' : '#d96767';
modalDesc.textContent = message;
resetModalState();
modalConfirmBtn.textContent = 'Konfirmasi';
modalCancelBtn.textContent = 'Batal';
modalConfirmBtn.focus();
}

function closeModal() {
modalOverlay.classList.remove('active');
setTimeout(() => {
modalOverlay.hidden = true;
resetModalState();
}, 300);
}

function showCheckmarkAnimation(){
modalDesc.textContent = 'Pembayaran berhasil!';
checkmarkContainer.classList.add('show');
// Restart the SVG animations by resetting and re-adding classes
const circle = checkmarkContainer.querySelector('.checkmark-circle');
const path = checkmarkContainer.querySelector('.checkmark-path');
circle.style.animation = 'none';
path.style.animation = 'none';
void circle.offsetWidth; // trigger reflow
void path.offsetWidth;
circle.style.animation = 'circleDraw 0.8s forwards ease-out';
path.style.animation = 'pathDraw 0.7s 0.75s forwards ease-out';
}

async function handlePaymentConfirmation() {


if (!modalPaymentSelected) {
alert('Silakan pilih metode pembayaran terlebih dahulu.');
return;
}
paymentProcessing = true;
modalConfirmBtn.disabled = true;
modalCancelBtn.disabled = true;
paymentOptionsContainer.hidden = true;
paymentInstructions.hidden = true;
modalDesc.style.color = '#c9ad6a';

if(modalPaymentSelected === 'cash'){


modalDesc.textContent = 'Memproses pembayaran tunai…';
await delay(600);
showCheckmarkAnimation();
await delay(2000);
finalizePayment();
} else {
modalDesc.textContent = 'Menunggu konfirmasi pembayaran...';
await delay(3000);
showCheckmarkAnimation();
await delay(2000);
finalizePayment();
}
}

function delay(ms){
return new Promise(resolve => setTimeout(resolve, ms));
}
function finalizePayment(){
closeModal();
let change = paymentEntered - totalDue;
let changeMsg = '';
if(change > 0){
changeMsg = ` Kembalian: ${formatRupiah(change)}`;
}
messageEl.textContent = `Pembayaran berhasil!${changeMsg}`;
messageEl.className = 'message success';
cart = {};
renderCart();
paymentInput.value = '';
paymentInput.focus();
paymentProcessing = false;
}

navLeft.addEventListener('click', () => {
setCurrentCategory(currentCategoryIndex - 1);
});
navRight.addEventListener('click', () => {
setCurrentCategory(currentCategoryIndex + 1);
});

let startX = 0;
let isDragging = false;

categorySlider.addEventListener('touchstart', (e) => {


if(e.touches.length === 1) {
startX = e.touches[0].clientX;
isDragging = true;
}
});
categorySlider.addEventListener('touchmove', (e) => {
if(!isDragging) return;
const currentX = e.touches[0].clientX;
const deltaX = currentX - startX;
if (Math.abs(deltaX) > 40) {
if(deltaX > 0) {
setCurrentCategory(currentCategoryIndex - 1);
} else {
setCurrentCategory(currentCategoryIndex + 1);
}
isDragging = false;
}
});
categorySlider.addEventListener('touchend', () => {
isDragging = false;
});

categorySlider.addEventListener('keydown', (e) => {


if(e.key === 'ArrowRight' || e.key.toLowerCase() === 'd') {
e.preventDefault();
setCurrentCategory(currentCategoryIndex + 1);
}
else if(e.key === 'ArrowLeft' || e.key.toLowerCase() === 'a') {
e.preventDefault();
setCurrentCategory(currentCategoryIndex - 1);
}
});
payBtn.addEventListener('click', () => {
if (paymentProcessing) return;
messageEl.textContent = '';
const inputVal = parseInt(paymentInput.value);
if (isNaN(inputVal) || inputVal < 0) {
messageEl.textContent = 'Masukkan jumlah uang yang valid.';
messageEl.className = 'message error';
return;
}
totalDue = getTotalPrice();
paymentEntered = inputVal;
if (totalDue === 0) {
messageEl.textContent = 'Keranjang anda kosong.';
messageEl.className = 'message error';
return;
}
if (paymentEntered < totalDue) {
const less = totalDue - paymentEntered;
messageEl.textContent = `Uang kurang Rp ${formatRupiah(less).slice(3)}.
Silakan tambah uang.`;
messageEl.className = 'message error';
return;
}
showModal('Pembayaran Berhasil', `Total: ${formatRupiah(totalDue)}\nSilakan
pilih metode pembayaran:`, true);
});

paymentOptionsContainer.querySelectorAll('.payment-method').forEach(btn => {
btn.addEventListener('click', () => {
if(paymentProcessing) return;
clearPaymentSelection();
btn.classList.add('selected');
modalPaymentSelected = btn.getAttribute('data-method');
showPaymentInstructions(modalPaymentSelected);
});
});

modalCancelBtn.addEventListener('click', () => {
if(paymentProcessing) return;
closeModal();
});

modalConfirmBtn.addEventListener('click', () => {
if(paymentProcessing) return;
handlePaymentConfirmation();
});

modalOverlay.addEventListener('click', (e) => {


if(e.target === modalOverlay && !paymentProcessing) {
closeModal();
}
});

// Initialization
renderCategorySlider();
renderMenu();
renderCart();
</script>
</body>
</html>

You might also like