File manager - Edit - /home/webapp69.cm.in.th/u69319090038/Shop/index.php
Back
<?php require_once __DIR__ . '/config/config.php'; ?> <!DOCTYPE html> <html lang="th"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="Sarin Shop — ช้อปปิ้งออนไลน์ สินค้าหลายล้านชิ้น ราคาถูก จัดส่งทั่วไทย"> <title>Sarin Shop — ช้อปง่าย จ่ายปลอดภัย</title> <link rel="stylesheet" href="./public/css/style.css"> <style> /* ── Flash Sale Banner ─────────────────────────────────── */ .flash-banner { background:linear-gradient(135deg,#1a1a2e,#16213e,#0f3460); padding:.6rem 0; text-align:center; } .flash-banner-inner { display:inline-flex; align-items:center; gap:.75rem; color:#fff; font-size:.82rem; } .flash-badge { background:linear-gradient(135deg,#ff6b35,#ee4d2d); color:#fff; font-weight:800; font-size:.72rem; padding:.2rem .6rem; border-radius:4px; letter-spacing:.5px; } .flash-countdown { display:inline-flex; gap:.3rem; align-items:center; } .countdown-box { background:rgba(255,255,255,.15); color:#fff; font-weight:800; font-size:.9rem; padding:.2rem .5rem; border-radius:4px; min-width:32px; text-align:center; } .countdown-sep { color:rgba(255,255,255,.6); font-weight:700; } /* ── Category Bar ──────────────────────────────────────── */ .cat-bar { background:#fff; border-bottom:1px solid var(--border); padding:.4rem 0; overflow-x:auto; -webkit-overflow-scrolling:touch; white-space:nowrap; } .cat-bar::-webkit-scrollbar { display:none; } .cat-bar-inner { display:inline-flex; gap:.25rem; padding:0 1rem; } .cat-pill { display:inline-flex; align-items:center; gap:.4rem; padding:.45rem 1rem; border-radius:99px; font-size:.8rem; font-weight:600; color:var(--text-secondary); background:var(--bg); border:none; cursor:pointer; white-space:nowrap; transition:all .2s; } .cat-pill:hover, .cat-pill.active { background:linear-gradient(135deg,var(--brand),#ff6b35); color:#fff; } /* ── Hero ─────────────────────────────────────────────── */ .hero { background:linear-gradient(135deg,#ee4d2d 0%,#ff6b35 50%,#ffa040 100%); padding:2.5rem 1rem; text-align:center; position:relative; overflow:hidden; } .hero::before { content:''; position:absolute; inset:0; background:url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.04'%3E%3Ccircle cx='30' cy='30' r='10'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"); } .hero-content { position:relative; max-width:700px; margin:0 auto; } .hero-title { font-size:clamp(1.5rem,4vw,2.5rem); font-weight:800; color:#fff; margin-bottom:.5rem; line-height:1.2; text-shadow:0 2px 12px rgba(0,0,0,.2); } .hero-sub { color:rgba(255,255,255,.9); font-size:.95rem; margin-bottom:1.5rem; } .hero-search { display:flex; max-width:550px; margin:0 auto; border-radius:var(--r-full); overflow:hidden; box-shadow:0 8px 32px rgba(0,0,0,.25); } .hero-search input { flex:1; padding:.85rem 1.25rem; border:none; font-size:.95rem; outline:none; } .hero-search button { background:var(--brand-dark); color:#fff; border:none; padding:.85rem 1.75rem; font-weight:700; cursor:pointer; font-size:.95rem; transition:background .2s; white-space:nowrap; } .hero-search button:hover { background:#a32e10; } /* ── Trust Badges ─────────────────────────────────────── */ .trust-row { display:flex; justify-content:center; gap:2rem; padding:1rem; background:#fff; border-bottom:1px solid var(--border); flex-wrap:wrap; } .trust-item { display:flex; align-items:center; gap:.5rem; font-size:.8rem; color:var(--text-secondary); font-weight:500; } .trust-item span { font-size:1.1rem; } /* ── Section ──────────────────────────────────────────── */ .section-wrap { max-width:1200px; margin:0 auto; padding:0 1rem 2rem; } .section-head { display:flex; align-items:center; justify-content:space-between; padding:1.25rem 0 .75rem; } .section-title-bar { display:flex; align-items:center; gap:.5rem; font-size:1.05rem; font-weight:800; color:var(--text-primary); } .section-title-bar::before { content:''; display:block; width:4px; height:20px; background:linear-gradient(180deg,var(--brand),#ff6b35); border-radius:99px; } .see-all-link { font-size:.82rem; color:var(--brand); font-weight:600; } /* ── Flash Sale Products Row ──────────────────────────── */ .flash-row { display:flex; gap:.75rem; overflow-x:auto; padding-bottom:.75rem; -webkit-overflow-scrolling:touch; scroll-snap-type:x mandatory; } .flash-row::-webkit-scrollbar { height:4px; } .flash-row::-webkit-scrollbar-thumb { background:var(--brand); border-radius:99px; } /* ── Product Grid ─────────────────────────────────────── */ .product-grid-main { display:grid; grid-template-columns:repeat(auto-fill,minmax(180px,1fr)); gap:.75rem; } @media(max-width:480px){ .product-grid-main{ grid-template-columns:repeat(2,1fr); } } .pcard { background:#fff; border-radius:var(--r-md); overflow:hidden; cursor:pointer; transition:transform .2s,box-shadow .2s; box-shadow:var(--shadow-sm); scroll-snap-align:start; flex-shrink:0; width:180px; } .pcard:hover { transform:translateY(-3px); box-shadow:var(--shadow-md); } .pcard-img { width:100%; height:160px; object-fit:cover; background:var(--bg); position:relative; display:flex; align-items:center; justify-content:center; font-size:3.5rem; } .pcard-img img { width:100%; height:160px; object-fit:cover; } .pcard-badge { position:absolute; top:.4rem; left:.4rem; font-size:.65rem; font-weight:800; padding:.2rem .45rem; border-radius:3px; } .pcard-badge.sale { background:#ee4d2d; color:#fff; } .pcard-badge.new { background:#22c55e; color:#fff; } .pcard-badge.hot { background:#f59e0b; color:#fff; } .pcard-badge.flash{ background:linear-gradient(135deg,#1a1a2e,#0f3460); color:#fff; } .pcard-body { padding:.625rem; } .pcard-name { font-size:.78rem; font-weight:500; color:var(--text-primary); margin-bottom:.3rem; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; line-height:1.4; min-height:2.3rem; } .pcard-price { color:var(--brand); font-weight:800; font-size:.95rem; } .pcard-old { color:var(--text-muted); text-decoration:line-through; font-size:.72rem; margin-left:.3rem; } .pcard-meta { display:flex; justify-content:space-between; align-items:center; margin-top:.3rem; } .pcard-sold { font-size:.68rem; color:var(--text-muted); } .pcard-rating { font-size:.68rem; color:#f59e0b; font-weight:600; } .pcard-footer { display:flex; gap:.35rem; padding:.5rem .625rem .625rem; } .pcard-btn { flex:1; padding:.4rem .5rem; font-size:.72rem; border-radius:var(--r-sm); border:1.5px solid var(--brand); cursor:pointer; font-weight:600; transition:all .15s; white-space:nowrap; } .pcard-btn.outline { background:#fff; color:var(--brand); } .pcard-btn.outline:hover { background:var(--brand-pale); } .pcard-btn.filled { background:var(--brand); color:#fff; border-color:var(--brand); } .pcard-btn.filled:hover { background:var(--brand-dark); } /* Sold bar (flash sale) */ .sold-bar-wrap { padding:.25rem .625rem .5rem; } .sold-bar-bg { background:#f5f5f5; border-radius:99px; height:5px; overflow:hidden; } .sold-bar-fill { height:100%; background:linear-gradient(90deg,#f59e0b,#ee4d2d); border-radius:99px; transition:width 1s cubic-bezier(.34,1.56,.64,1); } .sold-bar-label { font-size:.65rem; color:var(--brand); font-weight:700; margin-top:.2rem; } /* ── Sort / Filter bar ────────────────────────────────── */ .sort-bar { display:flex; gap:.5rem; align-items:center; flex-wrap:wrap; margin-bottom:.875rem; } .sort-btn { padding:.38rem .875rem; border-radius:99px; border:1.5px solid var(--border); background:#fff; font-size:.78rem; cursor:pointer; color:var(--text-secondary); font-weight:500; transition:all .2s; } .sort-btn:hover, .sort-btn.active { border-color:var(--brand); color:var(--brand); background:var(--brand-pale); } .search-filter { flex:1; min-width:160px; max-width:280px; } .search-filter input { width:100%; padding:.38rem .875rem; border:1.5px solid var(--border); border-radius:99px; font-size:.78rem; outline:none; transition:border-color .2s; } .search-filter input:focus { border-color:var(--brand); } /* ── Empty state ──────────────────────────────────────── */ .no-products { grid-column:1/-1; text-align:center; padding:3rem 1rem; } .no-products-icon { font-size:3.5rem; margin-bottom:.75rem; } .no-products-title { font-size:1.05rem; font-weight:700; color:var(--text-primary); } .no-products-sub { font-size:.85rem; color:var(--text-muted); margin-top:.4rem; } /* ── Cart Drawer ──────────────────────────────────────── */ .cart-overlay { position:fixed; inset:0; background:rgba(0,0,0,.45); z-index:800; display:none; backdrop-filter:blur(2px); } .cart-overlay.open { display:block; } .cart-drawer { position:fixed; right:0; top:0; bottom:0; width:min(400px,100vw); background:#fff; z-index:801; display:flex; flex-direction:column; transform:translateX(100%); transition:transform .3s cubic-bezier(.4,0,.2,1); box-shadow:-8px 0 32px rgba(0,0,0,.15); } .cart-drawer.open { transform:translateX(0); } .cart-drawer-head { display:flex; align-items:center; justify-content:space-between; padding:1.25rem 1.25rem 1rem; border-bottom:1px solid var(--border); } .cart-drawer-title { font-weight:800; font-size:1rem; } .cart-drawer-close { background:none; border:none; font-size:1.5rem; cursor:pointer; color:var(--text-muted); line-height:1; padding:.25rem; } .cart-drawer-body { flex:1; overflow-y:auto; padding:1rem; } .cart-drawer-foot { padding:1rem 1.25rem; border-top:1px solid var(--border); } .cart-total-row { display:flex; justify-content:space-between; font-weight:700; font-size:1rem; margin-bottom:.875rem; } .cart-total-price { color:var(--brand); font-size:1.25rem; } .cart-item { display:flex; gap:.75rem; padding:.75rem 0; border-bottom:1px solid var(--border); } .cart-item-img { width:64px; height:64px; border-radius:var(--r-sm); object-fit:cover; background:var(--bg); flex-shrink:0; display:flex; align-items:center; justify-content:center; font-size:2rem; } .cart-item-img img { width:64px; height:64px; object-fit:cover; border-radius:var(--r-sm); } .cart-item-info { flex:1; min-width:0; } .cart-item-name { font-size:.82rem; font-weight:600; margin-bottom:.25rem; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden; } .cart-item-price { color:var(--brand); font-weight:700; font-size:.875rem; } .cart-qty-row { display:flex; align-items:center; gap:.5rem; margin-top:.35rem; } .qty-btn { width:24px; height:24px; border-radius:50%; border:1.5px solid var(--border); background:#fff; cursor:pointer; font-size:.85rem; font-weight:700; display:flex; align-items:center; justify-content:center; transition:all .15s; } .qty-btn:hover { border-color:var(--brand); color:var(--brand); } .qty-num { font-size:.82rem; font-weight:700; min-width:1.5rem; text-align:center; } .cart-item-remove { font-size:.7rem; color:var(--text-muted); cursor:pointer; background:none; border:none; padding:0; transition:color .15s; } .cart-item-remove:hover { color:var(--error); } .cart-empty { text-align:center; padding:3rem 1rem; } /* Skeleton loader */ @keyframes shimmer { 0%{background-position:-400px 0} 100%{background-position:400px 0} } .skeleton { background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%); background-size:800px 100%; animation:shimmer 1.5s infinite; border-radius:var(--r-sm); } /* Loading grid */ .pcard-skeleton { background:#fff; border-radius:var(--r-md); overflow:hidden; } .pcard-skeleton .s-img { height:160px; } .pcard-skeleton .s-body { padding:.625rem; display:flex; flex-direction:column; gap:.4rem; } .pcard-skeleton .s-line { height:.75rem; } .pcard-skeleton .s-line.short { width:60%; } </style> </head> <body> <!-- ── Flash Sale Banner ──────────────────────────────────────── --> <div class="flash-banner"> <div class="flash-banner-inner"> <span class="flash-badge">⚡ FLASH SALE</span> <span>ลดสูงสุด 90%! สิ้นสุดใน</span> <div class="flash-countdown"> <div class="countdown-box" id="cd-h">00</div> <span class="countdown-sep">:</span> <div class="countdown-box" id="cd-m">00</div> <span class="countdown-sep">:</span> <div class="countdown-box" id="cd-s">00</div> </div> <span>ชั่วโมง</span> </div> </div> <!-- ── Navbar ─────────────────────────────────────────────────── --> <nav class="navbar"> <a href="./index.php" class="nav-logo"> <span class="nav-logo-icon">🛒</span> Sarin Shop </a> <div class="nav-search"> <input type="text" id="search-input" placeholder="ค้นหาสินค้า..." aria-label="ค้นหา" autocomplete="off"> <button class="nav-search-btn" id="nav-search-btn" aria-label="ค้นหา">🔍</button> </div> <div class="nav-actions"> <div id="nav-guest" style="display:flex;gap:.5rem;align-items:center;"> <a href="./auth.php?tab=register" class="btn btn-outline btn-sm" style="color:#fff;border-color:rgba(255,255,255,.5);">สมัครสมาชิก</a> <a href="./auth.php?tab=login" class="btn btn-sm" style="background:rgba(255,255,255,.2);color:#fff;">เข้าสู่ระบบ</a> </div> <div id="nav-user" style="display:none;align-items:center;gap:.625rem;"> <div class="nav-icon-btn" id="nav-cart-btn" title="ตะกร้าสินค้า" style="cursor:pointer;"> <span class="icon">🛒</span> <span class="nav-badge" id="cart-badge" style="display:none;">0</span> </div> <div class="nav-user-dropdown" id="nav-user-dropdown"> <img id="nav-avatar" src="" alt="Avatar" class="nav-avatar"> <span id="nav-username" class="nav-user-name"></span> <span id="nav-role-badge" class="role-badge role-buyer"></span> </div> </div> <div class="nav-icon-btn" title="การแจ้งเตือน"><span class="icon">🔔</span></div> </div> </nav> <!-- ── Category Sub Nav ───────────────────────────────────────── --> <div class="cat-bar"> <div class="cat-bar-inner" id="cat-bar"> <button class="cat-pill active" data-cat="">🏠 ทั้งหมด</button> <button class="cat-pill" data-cat="แฟชั่น">👗 แฟชั่น</button> <button class="cat-pill" data-cat="อิเล็กทรอนิกส์">📱 อิเล็กทรอนิกส์</button> <button class="cat-pill" data-cat="ความงาม">💄 ความงาม</button> <button class="cat-pill" data-cat="บ้านและสวน">🏡 บ้านและสวน</button> <button class="cat-pill" data-cat="อาหารและเครื่องดื่ม">🍜 อาหาร</button> <button class="cat-pill" data-cat="กีฬา">⚽ กีฬา</button> <button class="cat-pill" data-cat="ของเล่น">🎮 ของเล่น</button> <button class="cat-pill" data-cat="อื่นๆ">📦 อื่นๆ</button> </div> </div> <!-- ── Hero ──────────────────────────────────────────────────── --> <section class="hero"> <div class="hero-content"> <h1 class="hero-title">สินค้าหลายล้านชิ้น ราคาพิเศษทุกวัน</h1> <p class="hero-sub">ช้อปปิ้งออนไลน์ปลอดภัย จัดส่งทั่วไทย พร้อมส่วนลดสุดพิเศษ</p> <div class="hero-search"> <input type="text" id="hero-search-input" placeholder="ค้นหาสินค้าที่ต้องการ..." aria-label="ค้นหาสินค้า"> <button id="hero-search-btn">🔍 ค้นหา</button> </div> </div> </section> <!-- ── Trust Badges ───────────────────────────────────────────── --> <div class="trust-row"> <div class="trust-item"><span>🔒</span> ระบบการชำระเงินปลอดภัย 100%</div> <div class="trust-item"><span>🚚</span> จัดส่งทั่วประเทศ ติดตามสถานะ Real-time</div> <div class="trust-item"><span>↩️</span> รับประกันคืนสินค้าภายใน 15 วัน</div> <div class="trust-item"><span>🏷️</span> โปรโมชั่นและส่วนลดพิเศษทุกวัน</div> </div> <!-- ── Flash Sale Section ────────────────────────────────────── --> <div class="section-wrap" id="flash-section" style="padding-top:1.25rem;"> <div class="section-head"> <div class="section-title-bar">⚡ Flash Sale วันนี้</div> <a href="#all-products" class="see-all-link" onclick="showAll()">ดูทั้งหมด →</a> </div> <div class="flash-row" id="flash-row"> <!-- Flash products rendered by JS --> <div class="pcard-skeleton skeleton" style="width:180px;min-width:180px;height:280px;"></div> <div class="pcard-skeleton skeleton" style="width:180px;min-width:180px;height:280px;"></div> <div class="pcard-skeleton skeleton" style="width:180px;min-width:180px;height:280px;"></div> <div class="pcard-skeleton skeleton" style="width:180px;min-width:180px;height:280px;"></div> <div class="pcard-skeleton skeleton" style="width:180px;min-width:180px;height:280px;"></div> </div> </div> <!-- ── All Products ──────────────────────────────────────────── --> <div class="section-wrap" id="all-products"> <div class="section-head"> <div class="section-title-bar" id="products-section-title">🛍️ สินค้าแนะนำ</div> </div> <div class="sort-bar"> <div class="search-filter"> <input type="text" id="filter-search" placeholder="🔍 ค้นหาในหมวดนี้..."> </div> <button class="sort-btn active" data-sort="newest">ใหม่ล่าสุด</button> <button class="sort-btn" data-sort="popular">ขายดี</button> <button class="sort-btn" data-sort="price_asc">ราคา ↑</button> <button class="sort-btn" data-sort="price_desc">ราคา ↓</button> </div> <div class="product-grid-main" id="product-grid"> <!-- Skeleton loaders --> <div class="pcard-skeleton"><div class="skeleton s-img"></div><div class="s-body"><div class="skeleton s-line"></div><div class="skeleton s-line short"></div></div></div> <div class="pcard-skeleton"><div class="skeleton s-img"></div><div class="s-body"><div class="skeleton s-line"></div><div class="skeleton s-line short"></div></div></div> <div class="pcard-skeleton"><div class="skeleton s-img"></div><div class="s-body"><div class="skeleton s-line"></div><div class="skeleton s-line short"></div></div></div> <div class="pcard-skeleton"><div class="skeleton s-img"></div><div class="s-body"><div class="skeleton s-line"></div><div class="skeleton s-line short"></div></div></div> <div class="pcard-skeleton"><div class="skeleton s-img"></div><div class="s-body"><div class="skeleton s-line"></div><div class="skeleton s-line short"></div></div></div> <div class="pcard-skeleton"><div class="skeleton s-img"></div><div class="s-body"><div class="skeleton s-line"></div><div class="skeleton s-line short"></div></div></div> </div> <div id="load-more-wrap" style="text-align:center;padding:1.5rem 0;display:none;"> <button class="btn btn-outline" id="load-more-btn" style="padding:.65rem 2rem;">โหลดเพิ่มเติม ↓</button> </div> </div> <!-- ── Footer ─────────────────────────────────────────────────── --> <footer class="footer"> <div class="footer-grid"> <div> <div class="footer-brand">🛒 Sarin Shop</div> <p class="footer-desc">แพลตฟอร์มช้อปปิ้งออนไลน์ที่ใหญ่ที่สุด ช้อปง่าย จ่ายปลอดภัย ส่งทั่วไทย</p> </div> <div> <div class="footer-title">บริการลูกค้า</div> <div class="footer-links"> <a href="#">ศูนย์ช่วยเหลือ</a> <a href="#">วิธีการซื้อสินค้า</a> <a href="#">ติดตามพัสดุ</a> <a href="#">การคืนและการคืนเงิน</a> </div> </div> <div> <div class="footer-title">เกี่ยวกับ Sarin Shop</div> <div class="footer-links"> <a href="#">เกี่ยวกับเรา</a> <a href="#">นโยบายความเป็นส่วนตัว</a> <a href="#">ข้อกำหนดการใช้บริการ</a> <a href="./account.php?upgrade=1">สมัครเป็นผู้ขาย</a> </div> </div> <div> <div class="footer-title">ติดตามเรา</div> <div class="footer-links"> <a href="#">📘 Facebook</a> <a href="#">📸 Instagram</a> <a href="#">▶️ YouTube</a> <a href="#">🐦 Twitter/X</a> </div> </div> </div> <div class="footer-bottom">© 2025 Sarin Shop. สงวนลิขสิทธิ์ทุกประการ.</div> </footer> <!-- ── Cart Drawer ─────────────────────────────────────────────── --> <div class="cart-overlay" id="cart-overlay" onclick="closeCart()"></div> <div class="cart-drawer" id="cart-drawer"> <div class="cart-drawer-head"> <div class="cart-drawer-title">🛒 ตะกร้าสินค้า</div> <button class="cart-drawer-close" onclick="closeCart()">×</button> </div> <div class="cart-drawer-body" id="cart-body"> <div class="cart-empty"><div style="font-size:3rem;margin-bottom:.75rem;">🛒</div><div style="font-weight:700;">ตะกร้าว่างเปล่า</div><div style="font-size:.85rem;color:var(--text-muted);margin-top:.4rem;">เพิ่มสินค้าที่ชื่นชอบลงในตะกร้า</div></div> </div> <div class="cart-drawer-foot" id="cart-foot" style="display:none;"> <div class="cart-total-row"> <span>รวมทั้งหมด</span> <span class="cart-total-price" id="cart-total">฿0</span> </div> <button class="btn btn-primary w-full" onclick="window.location.href='./checkout.php'" style="padding:.75rem;font-size:.95rem;font-weight:700;">ดำเนินการชำระเงิน →</button> </div> </div> <script type="module"> import { initAuth, Auth, redirectToLogin } from './public/js/api.js'; import { showToast, renderNavUser } from './public/js/ui.js'; import { api } from './public/js/api.js'; window.showToast = showToast; // ─── State ────────────────────────────────────────────────── let currentCat = ''; let currentSort = 'newest'; let currentPage = 1; let searchQuery = ''; let allPages = 1; let cartItems = []; // Dummy fallback products when DB has none const dummyProducts = [ { id:'d1', name:'หูฟังไร้สาย Bluetooth 5.0 เสียงดี กันน้ำ', price:299, sold_count:2500, category:'อิเล็กทรอนิกส์', image_url:'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400&h=400&fit=crop', _emoji:'🎧', _badge:'ลด 67%', _oldPrice:899 }, { id:'d2', name:'นาฬิกาสมาร์ทวอทช์ มอนิเตอร์สุขภาพ 24 ชม.', price:1299, sold_count:890, category:'อิเล็กทรอนิกส์', image_url:'https://images.unsplash.com/photo-1523275335684-37898b6baf30?w=400&h=400&fit=crop', _emoji:'⌚', _badge:'ขายดี', _oldPrice:2999 }, { id:'d3', name:'กระเป๋าสะพายข้างหนัง PU คุณภาพสูง ทนทาน', price:459, sold_count:3200, category:'แฟชั่น', image_url:'https://images.unsplash.com/photo-1548036328-c9fa89d128fa?w=400&h=400&fit=crop', _emoji:'👜', _badge:'ยอดนิยม',_oldPrice:950 }, { id:'d4', name:'รองเท้าผ้าใบสีขาว แฟชั่น ใส่สบาย พื้นนุ่ม', price:649, sold_count:1800, category:'แฟชั่น', image_url:'https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=400&h=400&fit=crop', _emoji:'👟', _badge:'ใหม่', _oldPrice:1200 }, { id:'d5', name:'ครีมกันแดด SPF 50+ PA++++ กันน้ำ 50ml', price:189, sold_count:5600, category:'ความงาม', image_url:'https://images.unsplash.com/photo-1556228578-0d85b1a4d571?w=400&h=400&fit=crop', _emoji:'🧴', _badge:'ลด 58%', _oldPrice:450 }, { id:'d6', name:'พัดลมตั้งโต๊ะ USB ปรับได้ 3 ระดับ เงียบ', price:229, sold_count:990, category:'บ้านและสวน', image_url:'https://images.unsplash.com/photo-1585771724684-38269d6639fd?w=400&h=400&fit=crop', _emoji:'🌀', _badge:'Flash', _oldPrice:499 }, { id:'d7', name:'ขวดน้ำสแตนเลส เก็บความเย็น 24 ชม. 750ml', price:349, sold_count:4100, category:'กีฬา', image_url:'https://images.unsplash.com/photo-1602143407151-7111542de6e8?w=400&h=400&fit=crop', _emoji:'🍶', _badge:'ขายดี', _oldPrice:699 }, { id:'d8', name:'แผ่นรองเมาส์ XXL ขนาดใหญ่ พิมพ์ลาย', price:129, sold_count:7800, category:'อิเล็กทรอนิกส์', image_url:'https://images.unsplash.com/photo-1612832021455-245704c6755a?w=400&h=400&fit=crop', _emoji:'🖱️', _badge:'ลด 57%', _oldPrice:299 }, { id:'d9', name:'ไฟ LED ห้องนอน สีสัน ควบคุมผ่าน App', price:199, sold_count:3400, category:'บ้านและสวน', image_url:'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&h=400&fit=crop', _emoji:'💡', _badge:'Hot', _oldPrice:499 }, { id:'d10', name:'หมอนรองคอ Memory Foam รักษาคอ', price:399, sold_count:1500, category:'บ้านและสวน', image_url:'https://images.unsplash.com/photo-1631157769375-7e49c0b22e50?w=400&h=400&fit=crop', _emoji:'🛏️', _badge:'แนะนำ', _oldPrice:850 }, { id:'d11', name:'โทรศัพท์มือถือ Android สเปกแรง 128GB', price:5990, sold_count:620, category:'อิเล็กทรอนิกส์', image_url:'https://images.unsplash.com/photo-1511707171634-5f897ff02aa9?w=400&h=400&fit=crop', _emoji:'📱', _badge:'ใหม่', _oldPrice:7990 }, { id:'d12', name:'ลิปสติกไฮยาลูรอน ติดทน 24 ชม. หลากสี', price:149, sold_count:8200, category:'ความงาม', image_url:'https://images.unsplash.com/photo-1586495777744-4e6232bf2263?w=400&h=400&fit=crop', _emoji:'💄', _badge:'ขายดี', _oldPrice:290 }, { id:'d13', name:'เสื้อยืด Oversize Cotton 100% ทรงสวย', price:199, sold_count:4300, category:'แฟชั่น', image_url:'https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=400&h=400&fit=crop', _emoji:'👕', _badge:'ลด 44%', _oldPrice:350 }, { id:'d14', name:'กล้องดิจิตอล Mirrorless คุณภาพ 24MP', price:12990,sold_count:210, category:'อิเล็กทรอนิกส์', image_url:'https://images.unsplash.com/photo-1516035069371-29a1b244cc32?w=400&h=400&fit=crop', _emoji:'📷', _badge:'แนะนำ', _oldPrice:17900}, { id:'d15', name:'สนีกเกอร์หนัง Premium วินเทจ สีน้ำตาล', price:1290, sold_count:760, category:'แฟชั่น', image_url:'https://images.unsplash.com/photo-1460353581641-37baddab0fa2?w=400&h=400&fit=crop', _emoji:'👞', _badge:'Hot', _oldPrice:2490 }, { id:'d16', name:'ชุดออกกำลังกาย Yoga ผ้ายืด ระบายอากาศดี', price:390, sold_count:2100, category:'กีฬา', image_url:'https://images.unsplash.com/photo-1506629082955-511b1aa562c8?w=400&h=400&fit=crop', _emoji:'🧘', _badge:'ใหม่', _oldPrice:690 }, { id:'d17', name:'บะหมี่กึ่งสำเร็จรูปเกาหลี รสเผ็ดจัดจ้าน (แพ็ค 5 ซอง)', price:145, sold_count:1200, category:'อาหารและเครื่องดื่ม', image_url:'https://images.unsplash.com/photo-1569718212165-3a8278d5f624?w=400&h=400&fit=crop', _emoji:'🍜', _badge:'Hot', _oldPrice:195 }, { id:'d18', name:'ชาไทยผงปรุงสำเร็จ ตรามือ สูตรต้นตำรับ 400g', price:75, sold_count:3100, category:'อาหารและเครื่องดื่ม', image_url:'https://images.unsplash.com/photo-1576092768241-dec231879fc3?w=400&h=400&fit=crop', _emoji:'🥤', _badge:'ขายดี', _oldPrice:120 }, { id:'d19', name:'ตัวต่อบล็อกไม้ เสริมพัฒนาการเด็ก 50 ชิ้น', price:350, sold_count:450, category:'ของเล่น', image_url:'https://images.unsplash.com/photo-1587654780291-39c9404d746b?w=400&h=400&fit=crop', _emoji:'🧩', _badge:'แนะนำ', _oldPrice:490 }, { id:'d20', name:'ตุ๊กตาหมีขนนุ่ม ขนาดใหญ่ 60 ซม.', price:290, sold_count:980, category:'ของเล่น', image_url:'https://images.unsplash.com/photo-1559251606-c623743a6d76?w=400&h=400&fit=crop', _emoji:'🧸', _badge:'ใหม่', _oldPrice:390 }, { id:'d21', name:'สมุดโน้ตปกหนัง A5 ถนอมสายตา แบบมีเส้น', price:99, sold_count:1500, category:'อื่นๆ', image_url:'https://images.unsplash.com/photo-1531346878377-a5be20888e57?w=400&h=400&fit=crop', _emoji:'📓', _badge:'Hot', _oldPrice:150 }, ]; // ─── Flash Sale Countdown ──────────────────────────────────── function startCountdown() { const target = new Date(); target.setHours(24, 0, 0, 0); function update() { const now = Date.now(); const diff = Math.max(0, target - now); const h = String(Math.floor(diff / 3600000)).padStart(2,'0'); const m = String(Math.floor((diff % 3600000) / 60000)).padStart(2,'0'); const s = String(Math.floor((diff % 60000) / 1000)).padStart(2,'0'); document.getElementById('cd-h').textContent = h; document.getElementById('cd-m').textContent = m; document.getElementById('cd-s').textContent = s; } update(); setInterval(update, 1000); } // ─── Product Card ──────────────────────────────────────────── function badgeClass(badge) { if (!badge) return ''; const b = badge.toLowerCase(); if (b.includes('flash')) return 'flash'; if (b.includes('ลด') || b.includes('sale')) return 'sale'; if (b.includes('ใหม่') || b.includes('new')) return 'new'; return 'hot'; } function discountPct(price, oldPrice) { if (!oldPrice || oldPrice <= price) return 0; return Math.round((1 - price / oldPrice) * 100); } function soldLabel(n) { if (n >= 1000) return `ขายแล้ว ${(n/1000).toFixed(1)}k`; return `ขายแล้ว ${n}`; } function buildCard(p, isFlash = false) { const emoji = p._emoji || '🛍️'; const badge = p._badge || (p.sold_count > 3000 ? 'ขายดี' : ''); const oldPrice = p._oldPrice || null; const disc = oldPrice ? discountPct(p.price, oldPrice) : 0; const imgHtml = p.image_url ? `<img src="${p.image_url}" alt="${p.name}" loading="lazy" style="width:100%;height:160px;object-fit:cover;display:block;" onerror="this.style.display='none';this.nextElementSibling.style.display='flex'"> <div style="display:none;align-items:center;justify-content:center;height:160px;font-size:3.5rem;">${emoji}</div>` : `<div style="display:flex;align-items:center;justify-content:center;height:160px;font-size:3.5rem;">${emoji}</div>`; const soldPct = Math.min(100, Math.round((p.sold_count / (p.sold_count + 50)) * 100)); const soldBar = isFlash ? `<div class="sold-bar-wrap"><div class="sold-bar-bg"><div class="sold-bar-fill" style="width:${soldPct}%"></div></div><div class="sold-bar-label">เหลืออีก ${Math.max(1, Math.floor(50 * (1 - soldPct/100)))} ชิ้น</div></div>` : ''; return ` <div class="pcard" data-id="${p.id}" onclick="goProduct(${p.id})"> <div class="pcard-img" style="position:relative;"> ${imgHtml} ${badge ? `<span class="pcard-badge ${badgeClass(badge)}">${disc ? `ลด${disc}%` : badge}</span>` : ''} </div> <div class="pcard-body"> <div class="pcard-name">${p.name}</div> <div> <span class="pcard-price">฿${Number(p.price).toLocaleString('th-TH')}</span> ${oldPrice ? `<span class="pcard-old">฿${Number(oldPrice).toLocaleString('th-TH')}</span>` : ''} </div> <div class="pcard-meta"> <span class="pcard-sold">${soldLabel(p.sold_count)}</span> <span class="pcard-rating">⭐ 4.${Math.max(5,Math.min(9, Math.floor(4 + (p.sold_count % 5))))}</span> </div> </div> ${soldBar} <div class="pcard-footer"> <button class="pcard-btn outline" onclick="handleCart(event,${p.id})">🛒 ตะกร้า</button> <button class="pcard-btn filled" onclick="handleBuy(event,${p.id})">ซื้อเลย</button> </div> </div>`; } // ─── Load Products ────────────────────────────────────────── async function loadProducts(append = false) { if (!append) { currentPage = 1; document.getElementById('load-more-wrap').style.display = 'none'; } const grid = document.getElementById('product-grid'); const titleEl = document.getElementById('products-section-title'); // 1. Render dummy immediately so there's never a blank screen if (!append) { let fb = [...dummyProducts]; if (currentCat) fb = fb.filter(p => p.category === currentCat); if (searchQuery) fb = fb.filter(p => p.name.includes(searchQuery)); if (currentSort === 'popular') fb.sort((a,b) => b.sold_count - a.sold_count); if (currentSort === 'price_asc') fb.sort((a,b) => a.price - b.price); if (currentSort === 'price_desc') fb.sort((a,b) => b.price - a.price); grid.innerHTML = fb.length ? fb.map(p => buildCard(p)).join('') : `<div class="no-products"><div class="no-products-icon">🔍</div><div class="no-products-title">ไม่พบสินค้า</div><div class="no-products-sub">ลองค้นหาด้วยคำอื่น</div></div>`; } // Update title if (currentCat) titleEl.textContent = `🛍️ ${currentCat}`; else if (searchQuery) titleEl.textContent = `🔍 ผลการค้นหา: "${searchQuery}"`; else titleEl.textContent = '🛍️ สินค้าแนะนำ'; // 2. Try real API — replace dummy if found try { const qs = `?cat=${encodeURIComponent(currentCat)}&q=${encodeURIComponent(searchQuery)}&sort=${currentSort}&page=${currentPage}&limit=20`; const res = await api.getProducts(qs); if (res && res.ok && res.data && res.data.products && res.data.products.length > 0) { allPages = res.data.pages || 1; const html = res.data.products.map(p => buildCard(p)).join(''); if (append) grid.insertAdjacentHTML('beforeend', html); else grid.innerHTML = html; if (currentPage < allPages) document.getElementById('load-more-wrap').style.display = 'block'; } } catch(_) { /* keep dummy */ } } // Flash Sale Row async function loadFlashSale() { const flashRow = document.getElementById('flash-row'); // Render dummy immediately flashRow.innerHTML = dummyProducts.slice(0, 8).map(p => buildCard(p, true)).join(''); // Try real API try { const res = await api.getProducts('?sort=popular&limit=10'); if (res && res.ok && res.data && res.data.products && res.data.products.length > 0) { flashRow.innerHTML = res.data.products.slice(0, 8).map(p => buildCard(p, true)).join(''); } } catch(_) { /* keep dummy */ } } window.showAll = function() { currentCat = ''; document.querySelectorAll('.cat-pill').forEach(b => b.classList.toggle('active', b.dataset.cat === '')); searchQuery = ''; document.getElementById('filter-search').value = ''; loadProducts(); }; // ─── Category filter ───────────────────────────────────────── document.getElementById('cat-bar').addEventListener('click', e => { const pill = e.target.closest('.cat-pill'); if (!pill) return; document.querySelectorAll('.cat-pill').forEach(b => b.classList.remove('active')); pill.classList.add('active'); currentCat = pill.dataset.cat || ''; searchQuery = ''; document.getElementById('filter-search').value = ''; loadProducts(); }); // ─── Sort ──────────────────────────────────────────────────── document.querySelectorAll('.sort-btn').forEach(btn => { btn.addEventListener('click', () => { document.querySelectorAll('.sort-btn').forEach(b => b.classList.remove('active')); btn.classList.add('active'); currentSort = btn.dataset.sort; loadProducts(); }); }); // ─── Search ────────────────────────────────────────────────── function doSearch(q) { searchQuery = q.trim(); currentCat = ''; document.querySelectorAll('.cat-pill').forEach(b => b.classList.toggle('active', !b.dataset.cat)); loadProducts(); document.getElementById('all-products').scrollIntoView({ behavior:'smooth' }); } document.getElementById('hero-search-btn').addEventListener('click', () => { doSearch(document.getElementById('hero-search-input').value); }); document.getElementById('hero-search-input').addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(e.target.value); }); document.getElementById('nav-search-btn').addEventListener('click', () => { doSearch(document.getElementById('search-input').value); }); document.getElementById('search-input').addEventListener('keydown', e => { if (e.key === 'Enter') doSearch(e.target.value); }); let filterTimer; document.getElementById('filter-search').addEventListener('input', e => { clearTimeout(filterTimer); filterTimer = setTimeout(() => { searchQuery = e.target.value.trim(); loadProducts(); }, 400); }); // ─── Load more ─────────────────────────────────────────────── document.getElementById('load-more-btn').addEventListener('click', () => { currentPage++; loadProducts(true); }); // ─── Cart ──────────────────────────────────────────────────── window.handleCart = async function(e, productId) { e.stopPropagation(); if (!Auth.isLoggedIn()) { window.location.href = './auth.php?msg=กรุณาเข้าสู่ระบบก่อน'; return; } const res = await api.addToCart({ product_id: productId, qty: 1 }); if (!res || !res.ok) { showToast(res?.data?.message || 'เกิดข้อผิดพลาด', 'error'); return; } showToast('เพิ่มลงตะกร้าแล้ว! 🛒', 'success'); await loadCart(); }; window.handleBuy = function(e, productId) { e.stopPropagation(); if (!Auth.isLoggedIn()) { window.location.href = './auth.php?msg=กรุณาเข้าสู่ระบบก่อน'; return; } window.location.href = `./checkout.php?product=${productId}`; }; window.goProduct = function(id) { if (typeof id === 'string' && id.startsWith('d')) return; // dummy // Future: window.location.href = `./product.php?id=${id}`; }; // Cart open/close window.openCart = async function() { if (!Auth.isLoggedIn()) { window.location.href = './auth.php'; return; } document.getElementById('cart-overlay').classList.add('open'); document.getElementById('cart-drawer').classList.add('open'); await loadCart(); }; window.closeCart = function() { document.getElementById('cart-overlay').classList.remove('open'); document.getElementById('cart-drawer').classList.remove('open'); }; async function loadCart() { if (!Auth.isLoggedIn()) return; const res = await api.getCart(); if (!res || !res.ok) return; cartItems = res.data.items || []; renderCart(cartItems, res.data.total || 0); updateCartBadge(cartItems.length); } function updateCartBadge(count) { const badge = document.getElementById('cart-badge'); if (count > 0) { badge.textContent = count; badge.style.display = ''; } else badge.style.display = 'none'; } function renderCart(items, total) { const body = document.getElementById('cart-body'); const foot = document.getElementById('cart-foot'); const totalEl = document.getElementById('cart-total'); if (!items.length) { body.innerHTML = `<div class="cart-empty"><div style="font-size:3rem;margin-bottom:.75rem;">🛒</div><div style="font-weight:700;">ตะกร้าว่างเปล่า</div><div style="font-size:.85rem;color:var(--text-muted);margin-top:.4rem;">เพิ่มสินค้าที่ชื่นชอบลงในตะกร้า</div></div>`; foot.style.display = 'none'; return; } body.innerHTML = items.map(item => ` <div class="cart-item"> <div class="cart-item-img"> ${item.image_url ? `<img src="${item.image_url}" alt="${item.name}">` : '🛍️'} </div> <div class="cart-item-info"> <div class="cart-item-name">${item.name}</div> <div class="cart-item-price">฿${Number(item.price).toLocaleString('th-TH')}</div> <div class="cart-qty-row"> <button class="qty-btn" onclick="changeQty(${item.product_id},${item.qty - 1})">−</button> <span class="qty-num">${item.qty}</span> <button class="qty-btn" onclick="changeQty(${item.product_id},${item.qty + 1})">+</button> <button class="cart-item-remove" onclick="removeItem(${item.product_id})">ลบ ✕</button> </div> </div> </div> `).join(''); totalEl.textContent = '฿' + Number(total).toLocaleString('th-TH', {minimumFractionDigits:0}); foot.style.display = ''; } window.changeQty = async function(productId, qty) { if (qty < 1) { await removeItem(productId); return; } const res = await api.updateCart({ product_id: productId, qty }); if (!res || !res.ok) { showToast('เกิดข้อผิดพลาด','error'); return; } await loadCart(); }; window.removeItem = async function(productId) { const res = await api.removeFromCart({ product_id: productId }); if (!res || !res.ok) { showToast('เกิดข้อผิดพลาด','error'); return; } showToast('ลบออกจากตะกร้าแล้ว','info'); await loadCart(); }; // ─── Init ──────────────────────────────────────────────────── (async () => { startCountdown(); const user = await initAuth(); renderNavUser(user); // Nav cart document.getElementById('nav-cart-btn')?.addEventListener('click', () => openCart()); document.getElementById('nav-user-dropdown')?.addEventListener('click', () => { window.location.href = './account.php'; }); if (user) await loadCart(); // Load products in parallel await Promise.all([loadFlashSale(), loadProducts()]); })(); </script> </body> </html>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.36 |
proxy
|
phpinfo
|
Settings