anycoder-adadf141 / index.html
puwanath's picture
Upload folder using huggingface_hub
b0b757e verified
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Smart POS System - ระบบขายปลีกอัจฉริยะ</title>
<!-- Google Fonts: Sarabun เพื่อความสวยงามและอ่านง่ายภาษาไทย -->
<link href="https://fonts.googleapis.com/css2?family=Sarabun:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #2563eb;
--secondary-color: #1e40af;
--accent-color: #f59e0b;
--bg-color: #f3f4f6;
--card-bg: #ffffff;
--text-main: #1f2937;
--text-muted: #6b7280;
--border-color: #e5e7eb;
--danger-color: #ef4444;
--success-color: #10b981;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--radius: 12px;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Sarabun', sans-serif;
}
body {
background-color: var(--bg-color);
color: var(--text-main);
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden; /* ป้องกันการเลื่อนหน้าจร้างเกิน */
}
/* Header */
header {
background-color: var(--card-bg);
padding: 1rem 2rem;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow: var(--shadow-sm);
z-index: 10;
}
.brand {
display: flex;
align-items: center;
gap: 12px;
font-size: 1.5rem;
font-weight: 700;
color: var(--primary-color);
}
.header-actions button {
background: none;
border: none;
font-size: 1.2rem;
color: var(--text-muted);
cursor: pointer;
transition: color 0.3s;
}
.header-actions button:hover {
color: var(--primary-color);
}
/* Main Layout */
main {
display: flex;
flex: 1;
overflow: hidden;
gap: 20px;
padding: 20px;
}
/* Left Side: Product Grid */
.product-section {
flex: 2;
display: flex;
flex-direction: column;
gap: 15px;
overflow-y: auto;
padding-right: 5px;
}
/* Search Bar */
.search-container {
position: relative;
margin-bottom: 10px;
}
.search-input {
width: 100%;
padding: 12px 20px 12px 45px;
border: 1px solid var(--border-color);
border-radius: var(--radius);
font-size: 1rem;
background-color: var(--card-bg);
box-shadow: var(--shadow-sm);
transition: all 0.3s ease;
}
.search-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.search-icon {
position: absolute;
left: 15px;
top: 50%;
transform: translateY(-50%);
color: var(--text-muted);
}
/* Product Grid */
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 15px;
padding-bottom: 20px;
}
.product-card {
background: var(--card-bg);
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow-sm);
transition: transform 0.2s, box-shadow 0.2s;
cursor: pointer;
display: flex;
flex-direction: column;
position: relative;
}
.product-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.product-img {
width: 100%;
height: 120px;
object-fit: cover;
background-color: #e5e7eb;
}
.product-info {
padding: 12px;
flex: 1;
display: flex;
flex-direction: column;
}
.product-name {
font-weight: 600;
font-size: 1rem;
margin-bottom: 4px;
color: var(--text-main);
}
.product-price {
font-size: 1.1rem;
color: var(--primary-color);
font-weight: 700;
margin-top: auto;
}
.add-btn {
margin-top: 10px;
width: 100%;
padding: 8px;
background-color: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
font-weight: 500;
transition: background 0.2s;
}
.add-btn:hover {
background-color: var(--secondary-color);
}
/* Right Side: Cart */
.cart-section {
flex: 1;
background: var(--card-bg);
border-radius: var(--radius);
box-shadow: var(--shadow-md);
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 350px;
}
.cart-header {
padding: 20px;
border-bottom: 1px solid var(--border-color);
display: flex;
justify-content: space-between;
align-items: center;
}
.cart-header h2 {
font-size: 1.25rem;
color: var(--text-main);
}
.cart-items {
flex: 1;
overflow-y: auto;
padding: 15px;
}
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid var(--border-color);
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from { opacity: 0; transform: translateX(20px); }
to { opacity: 1; transform: translateX(0); }
}
.cart-item-info {
flex: 1;
}
.cart-item-name {
font-weight: 600;
font-size: 0.95rem;
}
.cart-item-price {
font-size: 0.85rem;
color: var(--text-muted);
}
.cart-controls {
display: flex;
align-items: center;
gap: 10px;
}
.qty-btn {
width: 28px;
height: 28px;
border-radius: 50%;
border: 1px solid var(--border-color);
background: white;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.qty-btn:hover {
background-color: var(--bg-color);
border-color: var(--primary-color);
color: var(--primary-color);
}
.qty-value {
font-weight: 600;
width: 20px;
text-align: center;
}
.remove-btn {
color: var(--text-muted);
cursor: pointer;
padding: 5px;
transition: color 0.2s;
}
.remove-btn:hover {
color: var(--danger-color);
}
.cart-footer {
padding: 20px;
background-color: #f9fafb;
border-top: 1px solid var(--border-color);
}
.summary-row {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 0.95rem;
color: var(--text-muted);
}
.summary-row.total {
font-size: 1.4rem;
font-weight: 700;
color: var(--text-main);
margin-top: 15px;
padding-top: 15px;
border-top: 2px dashed var(--border-color);
}
.checkout-btn {
width: 100%;
padding: 15px;
background-color: var(--success-color);
color: white;
border: none;
border-radius: var(--radius);
font-size: 1.1rem;
font-weight: 600;
margin-top: 15px;
cursor: pointer;
transition: background 0.3s;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
}
.checkout-btn:hover {
background-color: #059669;
}
.checkout-btn:disabled {
background-color: #9ca3af;
cursor: not-allowed;
}
/* Modal / Toast */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 100;
opacity: 0;
visibility: hidden;
transition: all 0.3s;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal {
background: white;
padding: 30px;
border-radius: var(--radius);
width: 90%;
max-width: 400px;
text-align: center;
transform: scale(0.9);
transition: transform 0.3s;
}
.modal-overlay.active .modal {
transform: scale(1);
}
.modal-icon {
font-size: 3rem;
color: var(--success-color);
margin-bottom: 15px;
}
.modal-title {
font-size: 1.5rem;
margin-bottom: 10px;
}
.modal-body {
color: var(--text-muted);
margin-bottom: 20px;
}
.modal-actions {
display: flex;
gap: 10px;
justify-content: center;
}
.btn {
padding: 10px 20px;
border-radius: 8px;
border: none;
cursor: pointer;
font-weight: 500;
transition: opacity 0.2s;
}
.btn:hover {
opacity: 0.9;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-secondary {
background-color: var(--bg-color);
color: var(--text-main);
}
/* Responsive */
@media (max-width: 768px) {
main {
flex-direction: column;
padding: 10px;
gap: 10px;
}
.product-section {
flex: none;
height: 60vh;
}
.cart-section {
flex: none;
height: 40vh;
min-width: auto;
}
}
</style>
</head>
<body>
<!-- Header -->
<header>
<div class="brand">
<i class="fa-solid fa-cash-register"></i>
Smart POS
</div>
<div class="header-actions">
<button title="รีเซ็ตระบบ" onclick="resetSystem()">
<i class="fa-solid fa-rotate-right"></i>
</button>
<button title="เกี่ยวกับ" onclick="showAbout()">
<i class="fa-solid fa-circle-info"></i>
</button>
</div>
</header>
<!-- Main Content -->
<main>
<!-- Product Section -->
<section class="product-section">
<div class="search-container">
<i class="fa-solid fa-magnifying-glass search-icon"></i>
<input type="text" id="searchInput" class="search-input" placeholder="ค้นหาสินค้า (เช่น กาแฟ, ขนม)...">
</div>
<div id="productGrid" class="product-grid">
<!-- Products will be injected here via JS -->
</div>
</section>
<!-- Cart Section -->
<section class="cart-section">
<div class="cart-header">
<h2><i class="fa-solid fa-cart-shopping"></i> ตะกร้าสินค้า</h2>
<span id="cartCount" style="background: var(--primary-color); color: white; padding: 2px 8px; border-radius: 10px; font-size: 0.8rem;">0 รายการ</span>
</div>
<div id="cartItems" class="cart-items">
<!-- Cart items will be injected here -->
<div style="text-align: center; color: var(--text-muted); margin-top: 50px;">
<i class="fa-solid fa-basket-shopping" style="font-size: 3rem; margin-bottom: 10px; opacity: 0.3;"></i>
<p>ยังไม่มีสินค้าในตะกร้า</p>
</div>
</div>
<div class="cart-footer">
<div class="summary-row">
<span>ยอดรวมสินค้า</span>
<span id="subtotalDisplay">฿0.00</span>
</div>
<div class="summary-row">
<span>ภาษีมูลค่าเพิ่ม (7%)</span>
<span id="taxDisplay">฿0.00</span>
</div>
<div class="summary-row total">
<span>ยอดสุทธิ</span>
<span id="totalDisplay">฿0.00</span>
</div>
<button id="checkoutBtn" class="checkout-btn" onclick="openCheckoutModal()" disabled>
<i class="fa-solid fa-credit-card"></i> ชำระเงิน
</button>
</div>
</section>
</main>
<!-- Payment Modal -->
<div id="paymentModal" class="modal-overlay">
<div class="modal">
<div class="modal-icon"><i class="fa-solid fa-receipt"></i></div>
<h3 class="modal-title">ยืนยันการชำระเงิน</h3>
<p class="modal-body">ยอดชำระทั้งหมด: <strong id="modalTotal" style="color: var(--primary-color);">฿0.00</strong></p>
<div style="margin-bottom: 15px;">
<label style="display: block; margin-bottom: 5px; font-weight: 500;">วิธีการชำระ</label>
<select id="paymentMethod" class="search-input" style="padding: 10px;">
<option value="cash">เงินสด</option>
<option value="card">บัตรเครดิต</option>
</select>
</div>
<div id="cashInputGroup" style="margin-bottom: 15px;">
<label style="display: block; margin-bottom: 5px; font-weight: 500;">จำนวนเงินที่รับ</label>
<input type="number" id="cashReceived" class="search-input" placeholder="0.00" oninput="calculateChange()">
</div>
<div class="summary-row total" style="margin-top: 10px; border-top: none;">
<span>เงินทอน</span>
<span id="changeDisplay">฿0.00</span>
</div>
<div class="modal-actions">
<button class="btn btn-secondary" onclick="closeModal('paymentModal')">ยกเลิก</button>
<button class="btn btn-primary" onclick="processPayment()">ยืนยัน</button>
</div>
</div>
</div>
<!-- Success Modal -->
<div id="successModal" class="modal-overlay">
<div class="modal">
<div class="modal-icon"><i class="fa-solid fa-check-circle"></i></div>
<h3 class="modal-title">ชำระเงินสำเร็จ!</h3>
<p class="modal-body">ขอบคุณที่ใช้บริการ Smart POS</p>
<div style="background: #f3f4f6; padding: 15px; border-radius: 8px; margin-bottom: 20px;">
<p style="font-size: 0.9rem; margin-bottom: 5px;">รายการสินค้า</p>
<ul id="receiptList" style="text-align: left; font-size: 0.85rem; color: var(--text-muted); list-style: none;">
<!-- Receipt items -->
</ul>
</div>
<button class="btn btn-primary" onclick="closeModal('successModal')">ปิดหน้าต่าง</button>
</div>
</div>
<!-- About Modal -->
<div id="aboutModal" class="modal-overlay">
<div class="modal">
<div class="modal-icon"><i class="fa-solid fa-code"></i></div>
<h3 class="modal-title">Smart POS System</h3>
<p class="modal-body">ระบบ POS ระดับมืออาชีพ ที่สร้างด้วย HTML, CSS และ JavaScript แบบ Vanilla</p>
<p style="font-size: 0.9rem; color: var(--text-muted);">Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" style="color: var(--primary-color); text-decoration: none; font-weight: bold;">anycoder</a></p>
<button class="btn btn-primary" onclick="closeModal('aboutModal')">เข้าใจแล้ว</button>
</div>
</div>
<script>
// --- Data ---
const products = [
{ id: 1, name: "กาแฟโบราณ", price: 45, category: "drink", img: "https://picsum.photos/seed/coffee1/200/200" },
{ id: 2, name: "โกโก้อบเชย", price: 35, category: "drink", img: "https://picsum.photos/seed/cocoa/200/200" },
{ id: 3, name: "ชามะนาว", price: 40, category: "drink", img: "https://picsum.photos/seed/tea/200/200" },
{ id: 4, name: "น้ำผลไม้สด", price: 50, category: "drink", img: "https://picsum.photos/seed/juice/200/200" },
{ id: 5, name: "บราวนี่ชีส", price: 90, category: "food", img: "https://picsum.photos/seed/brownie/200/200" },
{ id: 6, name: "เค้กท็อปปิ้ง", price: 85, category: "food", img: "https://picsum.photos/seed/cake/200/200" },
{ id: 7, name: "แซนวิชปลาทูน่า", price: 110, category: "food", img: "https://picsum.photos/seed/sandwich/200/200" },
{ id: 8, name: "ไส้กรอก", price: 120, category: "food", img: "https://picsum.photos/seed/sausage/200/200" },
{ id: 9, name: "บัตรอาหาร", price: 5, category: "other", img: "https://picsum.photos/seed/card/200/200" },
{ id: 10, name: "ช้อนทอง", price: 15, category: "other", img: "https://picsum.photos/seed/spoon/200/200" },
{ id: 11, name: "ไข่ต้ม", price: 20, category: "other", img: "https://picsum.photos/seed/egg/200/200" },
{ id: 12, name: "โซดาไฮโดรเจน", price: 25, category: "drink", img: "https://picsum.photos/seed/soda/200/200" },
];
let cart = [];
// --- Init ---
document.addEventListener('DOMContentLoaded', () => {
renderProducts(products);
// Search Listener
document.getElementById('searchInput').addEventListener('input', (e) => {
const term = e.target.value.toLowerCase();
const filtered = products.filter(p => p.name.toLowerCase().includes(term));
renderProducts(filtered);
});
// Payment Method Listener
document.getElementById('paymentMethod').addEventListener('change', (e) => {
const cashGroup = document.getElementById('cashInputGroup');
if (e.target.value === 'card') {
cashGroup.style.display = 'none';
} else {
cashGroup.style.display = 'block';
}
calculateChange();
});
});
// --- Render Functions ---
function renderProducts(list) {
const grid = document.getElementById('productGrid');
grid.innerHTML = '';
list.forEach(product => {
const card = document.createElement('div');
card.className = 'product-card';
card.onclick = () => addToCart(product.id);
card.innerHTML = `
<img src="${product.img}" alt="${product.name}" class="product-img" loading="lazy">
<div class="product-info">
<div class="product-name">${product.name}</div>
<div class="product-price">฿${product.price.toFixed(2)}</div>
</div>
<button class="add-btn"><i class="fa-solid fa-plus"></i> เพิ่ม</button>
`;
grid.appendChild(card);
});
}
function renderCart() {
const container = document.getElementById('cartItems');
const countBadge = document.getElementById('cartCount');
const checkoutBtn = document.getElementById('checkoutBtn');
container.innerHTML = '';
if (cart.length === 0) {
container.innerHTML = `
<div style="text-align: center; color: var(--text-muted); margin-top: 50px;">
<i class="fa-solid fa-basket-shopping" style="font-size: 3rem; margin-bottom: 10px; opacity: 0.3;"></i>
<p>ยังไม่มีสินค้าในตะกร้า</p>
</div>`;
countBadge.textContent = '0 รายการ';
checkoutBtn.disabled = true;
updateTotals();
return;
}
let totalQty = 0;
cart.forEach(item => {
totalQty += item.qty;
const el = document.createElement('div');
el.className = 'cart-item';
el.innerHTML = `
<div class="cart-item-info">
<div class="cart-item-name">${item.name}</div>
<div class="cart-item-price">฿${item.price.toFixed(2)} x ${item.qty}</div>
</div>
<div class="cart-controls">
<button class="qty-btn" onclick="updateQty(${item.id}, -1)">-</button>
<span class="qty-value">${item.qty}</span>
<button class="qty-btn" onclick="updateQty(${item.id}, 1)">+</button>
<i class="fa-solid fa-trash remove-btn" onclick="removeFromCart(${item.id})"></i>
</div>
`;
container.appendChild(el);
});
countBadge.textContent = `${totalQty} รายการ`;
checkoutBtn.disabled = false;
updateTotals();
}
function updateTotals() {
const subtotal = cart.reduce((sum, item) => sum + (item.price * item.qty), 0);
const tax = subtotal * 0.07;
const total = subtotal + tax;
document.getElementById('subtotalDisplay').textContent = `฿${subtotal.toFixed(2)}`;
document.getElementById('taxDisplay').textContent = `฿${tax.toFixed(2)}`;
document.getElementById('totalDisplay').textContent = `฿${total.toFixed(2)}`;
return total;
}
// --- Cart Logic ---
function addToCart(id) {
const product = products.find(p => p.id === id);
const existing = cart.find(item => item.id === id);
if (existing) {
existing.qty++;
} else {
cart.push({ ...product, qty: 1 });
}
renderCart();
}
function updateQty(id, change) {
const item = cart.find(i => i.id === id);
if (!item) return;
item.qty += change;
if (item.qty <= 0) {
removeFromCart(id);
} else {
renderCart();
}
}
function removeFromCart(id) {
cart = cart.filter(item => item.id !== id);
renderCart();
}
function resetSystem() {
if(confirm('ต้องการรีเซ็ตตะกร้าสินค้าใหม่หรือไม่?')) {
cart = [];
renderCart();
}
}
// --- Payment Logic ---
function openCheckoutModal() {
const total = updateTotals();
document.getElementById('modalTotal').textContent = `฿${total.toFixed(2)}`;
document.getElementById('paymentMethod').value = 'cash';
document.getElementById('cashInput').value = '';
document.getElementById('cashInputGroup').style.display = 'block';
document.getElementById('changeDisplay').textContent = '฿0.00';
openModal('paymentModal');
document.getElementById('cashReceived').focus();
}
function calculateChange() {
const method = document.getElementById('paymentMethod').value;
const total = parseFloat(document.getElementById('modalTotal').textContent.replace('฿', '').replace(',', ''));
let cash = 0;
if (method === 'cash') {
cash = parseFloat(document.getElementById('cashReceived').value) || 0;
const change = cash - total;
document.getElementById('changeDisplay').textContent = `฿${change.toFixed(2)}`;
} else {
document.getElementById('changeDisplay').textContent = 'N/A';
}
}
function processPayment() {
const method = document.getElementById('paymentMethod').value;
const total = parseFloat(document.getElementById('modalTotal').textContent.replace('฿', '').replace(',', ''));
if (method === 'cash') {
const cash = parseFloat(document.getElementById('cashReceived').value);
if (isNaN(cash) || cash < total) {
alert('เงินที่รับไม่เพียงพอ');
return;
}
}
// Show Receipt
const receiptList = document.getElementById('receiptList');
receiptList.innerHTML = '';
cart.forEach(item => {
const li = document.createElement('li');
li.innerHTML = `<span>${item.name}</span> <span>฿${(item.price * item.qty).toFixed(2)}</span>`;
receiptList.appendChild(li);
});
// Clear Cart
cart = [];
renderCart();
closeModal('paymentModal');
openModal('successModal');
}
// --- UI Utilities ---
function openModal(id) {
document.getElementById(id).classList.add('active');
}
function closeModal(id) {
document.getElementById(id).classList.remove('active');
}
function showAbout() {
openModal('aboutModal');
}
// Close modal on outside click
window.onclick = function(event) {
if (event.target.classList.contains('modal-overlay')) {
event.target.classList.remove('active');
}
}
</script>
</body>
</html>