File manager - Edit - /home/webapp69.cm.in.th/u69319090002/Shop002/shoppee2.html
Back
<!DOCTYPE html> <html lang="th" data-theme="light"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>HORU — ค้นพบสไตล์ที่ใช่สำหรับคุณ</title> <meta name="description" content="HORU แหล่งรวมแฟชั่นพรีเมียม ราคาดี ส่งไว เลือกสรรแล้วเพื่อคุณโดยเฉพาะ"> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Thai:wght@300;400;500;600;700&family=Outfit:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <style> /* ===== CSS Variables ===== */ :root { --accent: #3b82f6; --accent-dark: #1d4ed8; --accent-glow: rgba(59, 130, 246, 0.35); --accent-soft: #eff6ff; --bg-page: #f1f5fb; --bg-card: #ffffff; --bg-glass: rgba(255, 255, 255, 0.72); --bg-nav: rgba(255, 255, 255, 0.88); --bg-input: #f8fafd; --bg-sidebar: #ffffff; --bg-tag: #eef2ff; --text-900: #0f172a; --text-700: #334155; --text-500: #64748b; --text-300: #94a3b8; --border: rgba(0, 0, 0, 0.07); --border-accent: rgba(59, 130, 246, 0.3); --shadow-sm: 0 1px 4px rgba(15, 30, 60, 0.06); --shadow-md: 0 4px 20px rgba(15, 30, 60, 0.10); --shadow-lg: 0 12px 40px rgba(15, 30, 60, 0.14); --shadow-glow: 0 8px 32px rgba(59, 130, 246, 0.22); --radius-sm: 8px; --radius-md: 14px; --radius-lg: 20px; --radius-xl: 28px; --nav-h: 68px; } [data-theme="dark"] { --accent: #60a5fa; --accent-dark: #3b82f6; --accent-glow: rgba(96, 165, 250, 0.3); --accent-soft: rgba(59, 130, 246, 0.12); --bg-page: #080e1c; --bg-card: #0f1929; --bg-glass: rgba(15, 25, 41, 0.82); --bg-nav: rgba(8, 14, 28, 0.92); --bg-input: #131e30; --bg-sidebar: #0f1929; --bg-tag: rgba(59, 130, 246, 0.15); --text-900: #f0f6ff; --text-700: #c8d9f0; --text-500: #7a9cc4; --text-300: #3d5a7a; --border: rgba(255, 255, 255, 0.06); --border-accent: rgba(96, 165, 250, 0.25); --shadow-sm: 0 1px 4px rgba(0, 0, 0, 0.4); --shadow-md: 0 4px 20px rgba(0, 0, 0, 0.5); --shadow-lg: 0 12px 40px rgba(0, 0, 0, 0.6); --shadow-glow: 0 8px 32px rgba(59, 130, 246, 0.25); } /* ===== Reset ===== */ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } html { scroll-behavior: smooth; } body { font-family: 'Noto Sans Thai', 'Outfit', sans-serif; background: var(--bg-page); color: var(--text-900); min-height: 100vh; transition: background 0.4s, color 0.4s; overflow-x: hidden; } /* ===== NAVBAR ===== */ .navbar { position: sticky; top: 0; z-index: 100; height: var(--nav-h); background: var(--bg-nav); backdrop-filter: blur(20px) saturate(180%); -webkit-backdrop-filter: blur(20px) saturate(180%); border-bottom: 1px solid var(--border); box-shadow: var(--shadow-sm); transition: background 0.4s, box-shadow 0.3s; } .navbar-inner { max-width: 1280px; margin: 0 auto; padding: 0 24px; height: 100%; display: flex; align-items: center; gap: 20px; } /* Logo */ .logo { text-decoration: none; display: flex; align-items: center; gap: 10px; flex-shrink: 0; } .logo-mark { width: 38px; height: 38px; background: linear-gradient(135deg, var(--accent) 0%, #6366f1 100%); border-radius: 10px; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 16px; font-weight: 800; box-shadow: 0 4px 14px var(--accent-glow); font-family: 'Outfit', sans-serif; letter-spacing: -1px; transition: transform 0.25s, box-shadow 0.25s; } .logo:hover .logo-mark { transform: scale(1.08) rotate(-3deg); box-shadow: 0 6px 20px var(--accent-glow); } .logo-text { font-family: 'Outfit', sans-serif; font-size: 22px; font-weight: 800; letter-spacing: -0.5px; color: var(--text-900); transition: color 0.3s; } .logo-text span { color: var(--accent); } /* Search */ .nav-search { flex: 1; max-width: 440px; position: relative; } .nav-search input { width: 100%; height: 42px; background: var(--bg-input); border: 1.5px solid var(--border); border-radius: 100px; padding: 0 50px 0 18px; font-size: 13px; font-family: inherit; color: var(--text-900); outline: none; transition: border-color 0.2s, box-shadow 0.2s, background 0.3s; } .nav-search input::placeholder { color: var(--text-300); } .nav-search input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); background: var(--bg-card); } .nav-search-btn { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); width: 32px; height: 32px; background: linear-gradient(135deg, var(--accent), #6366f1); border: none; border-radius: 50%; color: #fff; font-size: 12px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: transform 0.2s, box-shadow 0.2s; } .nav-search-btn:hover { transform: translateY(-50%) scale(1.1); box-shadow: 0 4px 12px var(--accent-glow); } /* Nav Actions */ .nav-actions { display: flex; align-items: center; gap: 12px; margin-left: auto; } .sell-btn { display: inline-flex; align-items: center; gap: 6px; height: 38px; padding: 0 16px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #fff; border: none; border-radius: var(--radius-sm); font-size: 12.5px; font-weight: 600; font-family: inherit; cursor: pointer; transition: all 0.2s; box-shadow: 0 4px 12px rgba(16, 185, 129, 0.2); white-space: nowrap; } .sell-btn:hover { transform: translateY(-1.5px); box-shadow: 0 6px 16px rgba(16, 185, 129, 0.35); } .nav-icon-btn { width: 40px; height: 40px; border-radius: var(--radius-sm); background: transparent; border: 1.5px solid var(--border); color: var(--text-700); font-size: 15px; cursor: pointer; display: flex; align-items: center; justify-content: center; position: relative; transition: all 0.2s; } .nav-icon-btn:hover { background: var(--accent-soft); border-color: var(--border-accent); color: var(--accent); } .nav-badge { position: absolute; top: -4px; right: -4px; background: var(--accent); color: #fff; font-size: 9px; font-weight: 700; min-width: 17px; height: 17px; border-radius: 50%; display: flex; align-items: center; justify-content: center; border: 2px solid var(--bg-card); font-family: 'Outfit', sans-serif; } .theme-pill { display: flex; align-items: center; gap: 6px; padding: 0 14px; height: 36px; border-radius: 100px; background: var(--bg-input); border: 1.5px solid var(--border); color: var(--text-700); font-size: 12px; font-family: inherit; font-weight: 500; cursor: pointer; transition: all 0.2s; white-space: nowrap; } .theme-pill:hover { border-color: var(--accent); color: var(--accent); } .theme-label-dark { display: none; } [data-theme="dark"] .theme-label-light { display: none; } [data-theme="dark"] .theme-label-dark { display: inline; } /* Login Button style */ .login-btn { display: inline-flex; align-items: center; gap: 6px; height: 38px; padding: 0 16px; background: linear-gradient(135deg, var(--accent) 0%, #6366f1 100%); color: #fff; border: none; border-radius: var(--radius-sm); font-size: 12.5px; font-weight: 600; font-family: inherit; cursor: pointer; transition: all 0.2s; box-shadow: var(--shadow-sm); white-space: nowrap; } .login-btn:hover { transform: translateY(-1.5px); box-shadow: var(--shadow-glow); } /* User Profile Dropdown styles */ .user-profile-container { position: relative; display: inline-block; } .user-avatar-btn { display: flex; align-items: center; gap: 8px; height: 38px; padding: 0 12px; background: var(--bg-input); border: 1.5px solid var(--border); border-radius: var(--radius-sm); cursor: pointer; font-family: inherit; color: var(--text-900); transition: all 0.2s; outline: none; } .user-avatar-btn:hover { border-color: var(--accent); background: var(--bg-card); } .user-avatar-btn img { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; border: 1px solid var(--border); } .user-avatar-btn span { font-size: 12.5px; font-weight: 600; } .user-dropdown { position: absolute; top: calc(100% + 8px); right: 0; width: 190px; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-md); box-shadow: var(--shadow-md); display: none; flex-direction: column; overflow: hidden; z-index: 120; animation: slideDown 0.2s cubic-bezier(0.16, 1, 0.3, 1); } .user-dropdown.show { display: flex; } .dropdown-item { padding: 10px 16px; font-size: 12.5px; color: var(--text-700); text-decoration: none; background: transparent; border: none; text-align: left; font-family: inherit; cursor: pointer; transition: background 0.15s, color 0.15s; display: flex; align-items: center; gap: 8px; outline: none; } .dropdown-item:hover { background: var(--accent-soft); color: var(--accent); } .dropdown-item.logout { border-top: 1px solid var(--border); color: #ef4444; } .dropdown-item.logout:hover { background: rgba(239, 68, 68, 0.08); color: #ef4444; } @keyframes slideDown { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); } } /* ===== HERO BANNER ===== */ .hero { background: linear-gradient(135deg, #0f172a 0%, #1e3a8a 40%, #312e81 70%, #1e40af 100%); padding: 48px 24px; position: relative; overflow: hidden; } .hero::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse 60% 80% at 80% 50%, rgba(99, 102, 241, 0.25) 0%, transparent 70%), radial-gradient(ellipse 40% 60% at 20% 30%, rgba(59, 130, 246, 0.2) 0%, transparent 60%); } .hero-grid { max-width: 1280px; margin: 0 auto; display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 40px; position: relative; z-index: 1; } .hero-eyebrow { display: inline-flex; align-items: center; gap: 6px; background: rgba(99, 102, 241, 0.2); border: 1px solid rgba(99, 102, 241, 0.4); border-radius: 100px; padding: 4px 12px; font-size: 11px; color: #a5b4fc; font-weight: 600; letter-spacing: 0.5px; margin-bottom: 16px; text-transform: uppercase; } .hero h1 { font-family: 'Outfit', sans-serif; font-size: 42px; font-weight: 800; color: #fff; line-height: 1.15; letter-spacing: -1px; margin-bottom: 12px; } .hero h1 em { font-style: normal; background: linear-gradient(135deg, #93c5fd, #a5b4fc); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; } .hero-sub { color: rgba(255, 255, 255, 0.65); font-size: 14px; line-height: 1.7; max-width: 420px; margin-bottom: 28px; } .hero-tags { display: flex; flex-wrap: wrap; gap: 8px; } .hero-tag { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.15); border-radius: 100px; padding: 6px 14px; font-size: 12px; color: rgba(255, 255, 255, 0.75); cursor: pointer; transition: all 0.2s; text-decoration: none; } .hero-tag:hover { background: rgba(255, 255, 255, 0.16); color: #fff; } .hero-stats { display: flex; flex-direction: column; gap: 14px; } .stat-card { background: rgba(255, 255, 255, 0.07); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.12); border-radius: var(--radius-md); padding: 16px 22px; text-align: center; min-width: 120px; } .stat-num { font-family: 'Outfit', sans-serif; font-size: 26px; font-weight: 800; color: #fff; line-height: 1; } .stat-label { font-size: 11px; color: rgba(255, 255, 255, 0.5); margin-top: 4px; } /* ===== PAGE LAYOUT ===== */ .page-wrap { max-width: 1280px; margin: 0 auto; padding: 28px 24px 60px; } /* ===== SIDEBAR ===== */ .sidebar { position: sticky; top: calc(var(--nav-h) + 20px); height: fit-content; } .sidebar-card { background: var(--bg-card); border-radius: var(--radius-lg); border: 1px solid var(--border); box-shadow: var(--shadow-sm); overflow: hidden; } .sidebar-head { padding: 18px 20px 14px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 13px; color: var(--text-900); } .sidebar-head i { color: var(--accent); font-size: 14px; } .filter-block { padding: 16px 20px; border-bottom: 1px solid var(--border); } .filter-block:last-child { border-bottom: none; } .filter-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; color: var(--text-300); margin-bottom: 10px; } .filter-label { display: flex; align-items: center; gap: 9px; font-size: 12.5px; color: var(--text-500); cursor: pointer; padding: 5px 8px; border-radius: var(--radius-sm); transition: background 0.15s, color 0.15s; margin-bottom: 2px; } .filter-label:hover { background: var(--accent-soft); color: var(--accent); } .filter-label input[type="checkbox"] { accent-color: var(--accent); width: 14px; height: 14px; cursor: pointer; } .price-inputs { display: flex; gap: 8px; align-items: center; margin-top: 8px; } .price-inputs input { flex: 1; min-width: 0; width: 100%; height: 34px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); padding: 0 10px; font-size: 12px; background: var(--bg-input); color: var(--text-900); font-family: inherit; outline: none; transition: border-color 0.2s; } .price-inputs input:focus { border-color: var(--accent); } .price-inputs span { font-size: 13px; color: var(--text-300); flex-shrink: 0; } .apply-btn { width: 100%; margin-top: 10px; height: 36px; background: linear-gradient(135deg, var(--accent), #6366f1); color: #fff; border: none; border-radius: var(--radius-sm); font-size: 12px; font-family: inherit; font-weight: 600; cursor: pointer; transition: opacity 0.2s, transform 0.15s; box-shadow: 0 3px 10px var(--accent-glow); } .apply-btn:hover { opacity: 0.9; transform: translateY(-1px); } .rating-row { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--text-500); cursor: pointer; padding: 5px 8px; border-radius: var(--radius-sm); margin-bottom: 2px; transition: background 0.15s; } .rating-row:hover { background: var(--accent-soft); } .rating-row .stars { color: #fbbf24; font-size: 11px; } /* ===== MAIN SECTION ===== */ .main-section { min-width: 0; } /* Sort Bar */ .sort-bar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px; gap: 12px; flex-wrap: wrap; } .sort-left { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } .sort-label { font-size: 12px; color: var(--text-500); font-weight: 500; } .sort-btn { height: 34px; padding: 0 16px; border-radius: 100px; font-size: 12px; font-family: inherit; font-weight: 500; border: 1.5px solid var(--border); background: var(--bg-card); color: var(--text-500); cursor: pointer; transition: all 0.2s; } .sort-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 3px 10px var(--accent-glow); } .sort-btn:not(.active):hover { border-color: var(--accent); color: var(--accent); } .sort-select { height: 34px; padding: 0 10px; border: 1.5px solid var(--border); border-radius: 100px; font-size: 12px; font-family: inherit; background: var(--bg-card); color: var(--text-700); cursor: pointer; outline: none; } .sort-select:focus { border-color: var(--accent); } /* Pagination */ .pagination { display: flex; align-items: center; gap: 6px; } .page-info { font-size: 12px; color: var(--text-500); } .page-info strong { color: var(--accent); font-family: 'Outfit', sans-serif; } .page-btn { width: 30px; height: 30px; border-radius: var(--radius-sm); background: var(--bg-card); border: 1.5px solid var(--border); color: var(--text-500); font-size: 12px; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; justify-content: center; } .page-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); } .page-btn:disabled { opacity: 0.4; cursor: not-allowed; } /* ===== PRODUCT GRID ===== */ .product-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; } /* ===== PRODUCT CARD ===== */ .product-card { background: var(--bg-card); border-radius: var(--radius-lg); overflow: hidden; border: 1px solid var(--border); box-shadow: var(--shadow-sm); display: flex; flex-direction: column; position: relative; transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.3s ease, border-color 0.25s; cursor: pointer; } .product-card:hover { transform: translateY(-6px) scale(1.01); box-shadow: var(--shadow-lg), 0 0 0 1px var(--border-accent); border-color: var(--border-accent); } /* Image area */ .card-img { position: relative; overflow: hidden; aspect-ratio: 1 / 1; background: var(--bg-input); } .card-img img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94); display: block; } .product-card:hover .card-img img { transform: scale(1.08); } /* Overlay gradient on hover */ .card-img::after { content: ''; position: absolute; inset: 0; background: linear-gradient(to top, rgba(15, 23, 42, 0.55) 0%, transparent 50%); opacity: 0; transition: opacity 0.3s; } .product-card:hover .card-img::after { opacity: 1; } /* Badge */ .card-badge { position: absolute; top: 10px; left: 10px; font-size: 10px; font-weight: 700; padding: 3px 9px; border-radius: 100px; letter-spacing: 0.3px; z-index: 2; backdrop-filter: blur(8px); font-family: 'Outfit', sans-serif; } .badge-official { background: rgba(37, 99, 235, 0.85); color: #fff; border: 1px solid rgba(255, 255, 255, 0.2); } .badge-hot { background: rgba(249, 115, 22, 0.85); color: #fff; border: 1px solid rgba(255, 255, 255, 0.2); } /* Wishlist */ .wishlist-btn { position: absolute; top: 10px; right: 10px; width: 32px; height: 32px; background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(8px); border: none; border-radius: 50%; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-300); font-size: 13px; opacity: 0; transform: scale(0.7); transition: all 0.25s; z-index: 2; } .product-card:hover .wishlist-btn { opacity: 1; transform: scale(1); } .wishlist-btn:hover { color: #ef4444; background: #fff; } .wishlist-btn.active { color: #ef4444; opacity: 1; transform: scale(1); } /* Quick-add button (on hover) */ .quick-add { position: absolute; bottom: 10px; left: 50%; transform: translateX(-50%) translateY(10px); white-space: nowrap; background: rgba(255, 255, 255, 0.92); backdrop-filter: blur(8px); border: none; border-radius: 100px; padding: 7px 18px; font-size: 11.5px; font-weight: 600; font-family: inherit; color: var(--accent-dark); cursor: pointer; opacity: 0; transition: all 0.28s; z-index: 3; box-shadow: var(--shadow-md); } .product-card:hover .quick-add { opacity: 1; transform: translateX(-50%) translateY(0); } /* Card Body */ .card-body { padding: 14px 14px 16px; flex: 1; display: flex; flex-direction: column; gap: 6px; } .card-brand { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.8px; color: var(--accent); font-family: 'Outfit', sans-serif; } .card-title { font-size: 13px; font-weight: 500; color: var(--text-700); line-height: 1.5; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; flex-shrink: 0; } /* Sizes layout inside the product card */ .size-selector { display: flex; flex-wrap: wrap; gap: 4px; margin: 6px 0; } .size-btn { height: 24px; min-width: 28px; padding: 0 6px; border-radius: 4px; border: 1px solid var(--border); font-size: 10.5px; font-weight: 600; font-family: 'Outfit', sans-serif; background: var(--bg-input); color: var(--text-700); cursor: pointer; transition: all 0.2s; display: inline-flex; align-items: center; justify-content: center; } .size-btn:hover { border-color: var(--accent); color: var(--accent); } .size-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 2px 6px var(--accent-glow); } .card-price-row { display: flex; align-items: baseline; gap: 7px; margin-top: 2px; } .price-now { font-family: 'Outfit', sans-serif; font-size: 18px; font-weight: 800; color: var(--text-900); } .price-was { font-size: 11px; color: var(--text-300); text-decoration: line-through; } .price-save { margin-left: auto; font-size: 10px; font-weight: 700; color: #10b981; background: rgba(16, 185, 129, 0.1); border-radius: 100px; padding: 2px 7px; font-family: 'Outfit', sans-serif; } .card-meta { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text-300); } .card-meta .stars { color: #fbbf24; font-size: 10px; } .card-meta .rating { color: var(--text-500); font-weight: 600; } .card-meta .dot { width: 3px; height: 3px; background: var(--border); border-radius: 50%; } .card-meta .sold { margin-left: auto; } /* Cart Controls */ .card-footer { padding: 0 14px 14px; display: flex; flex-direction: column; gap: 8px; } .qty-row { display: flex; align-items: center; justify-content: space-between; } .qty-label { font-size: 11px; color: var(--text-500); } .qty-ctrl { display: flex; align-items: center; border: 1.5px solid var(--border); border-radius: var(--radius-sm); overflow: hidden; background: var(--bg-input); transition: border-color 0.2s; } .qty-ctrl:focus-within { border-color: var(--accent); } .qty-btn { width: 28px; height: 28px; background: transparent; border: none; cursor: pointer; font-size: 14px; color: var(--text-500); display: flex; align-items: center; justify-content: center; transition: background 0.15s, color 0.15s; font-family: inherit; } .qty-btn:hover { background: var(--accent-soft); color: var(--accent); } .qty-input { width: 34px; text-align: center; border: none; outline: none; font-size: 13px; font-weight: 600; background: transparent; color: var(--text-900); font-family: 'Outfit', sans-serif; padding: 0; } .add-btn { width: 100%; height: 38px; background: linear-gradient(135deg, var(--accent) 0%, #6366f1 100%); color: #fff; border: none; border-radius: var(--radius-sm); font-size: 12px; font-weight: 600; font-family: inherit; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 7px; transition: opacity 0.2s, transform 0.15s, box-shadow 0.2s; box-shadow: 0 3px 12px var(--accent-glow); } .add-btn:hover { opacity: 0.92; transform: translateY(-1px); box-shadow: 0 6px 18px var(--accent-glow); } .add-btn:active { transform: translateY(0); } /* ===== ADD PRODUCT MODAL ===== */ .modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 15, 0.6); backdrop-filter: blur(8px); z-index: 210; display: none; align-items: center; justify-content: center; padding: 20px; opacity: 0; transition: opacity 0.3s; } .modal-overlay.show { display: flex; opacity: 1; } .modal-box { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-lg); width: 100%; max-width: 500px; box-shadow: var(--shadow-lg); display: flex; flex-direction: column; overflow: hidden; transform: translateY(20px); transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); } .modal-overlay.show .modal-box { transform: translateY(0); } .modal-header { padding: 18px 24px; border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; } .modal-header h3 { font-family: 'Outfit', sans-serif; font-size: 16px; font-weight: 700; color: var(--text-900); } .modal-close-btn { width: 32px; height: 32px; border-radius: 50%; background: var(--bg-input); border: none; color: var(--text-500); cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; } .modal-close-btn:hover { background: var(--accent-soft); color: var(--accent); } .modal-body { padding: 20px 24px; overflow-y: auto; max-height: 70vh; display: flex; flex-direction: column; gap: 14px; } .form-group { display: flex; flex-direction: column; gap: 6px; } .form-group label { font-size: 12.5px; font-weight: 600; color: var(--text-700); } .form-group input, .form-group select { height: 38px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); padding: 0 12px; font-size: 13px; background: var(--bg-input); color: var(--text-900); font-family: inherit; outline: none; transition: border-color 0.2s; } .form-group input:focus, .form-group select:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); } .form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } .checkbox-group-wrapper { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 4px; } .checkbox-group { display: flex; flex-direction: row; gap: 6px; align-items: center; } .checkbox-group input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; } .checkbox-group label { font-size: 13px; font-weight: 500; color: var(--text-700); cursor: pointer; } .modal-footer { padding: 16px 24px; border-top: 1px solid var(--border); display: flex; justify-content: flex-end; gap: 10px; } .btn-cancel { height: 38px; padding: 0 16px; background: var(--bg-input); border: 1.5px solid var(--border); border-radius: var(--radius-sm); color: var(--text-700); font-size: 13px; font-weight: 600; cursor: pointer; transition: all 0.2s; } .btn-cancel:hover { background: var(--border); } .btn-submit { height: 38px; padding: 0 20px; background: linear-gradient(135deg, var(--accent) 0%, #6366f1 100%); border: none; border-radius: var(--radius-sm); color: #fff; font-size: 13px; font-weight: 600; cursor: pointer; box-shadow: 0 4px 12px var(--accent-glow); transition: all 0.2s; } .btn-submit:hover { opacity: 0.9; transform: translateY(-1px); } /* ===== CART PANEL ===== */ .cart-overlay { position: fixed; inset: 0; background: rgba(0, 0, 14, 0.5); backdrop-filter: blur(4px); z-index: 200; opacity: 0; pointer-events: none; transition: opacity 0.3s; } .cart-overlay.show { opacity: 1; pointer-events: all; } .cart-panel { position: fixed; top: 0; right: 0; height: 100%; width: 360px; background: var(--bg-card); z-index: 201; transform: translateX(100%); transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1); display: flex; flex-direction: column; box-shadow: -8px 0 40px rgba(0, 0, 0, 0.25); } .cart-panel.open { transform: translateX(0); } .cart-head { padding: 20px 22px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); } .cart-head h3 { font-family: 'Outfit', sans-serif; font-size: 16px; font-weight: 700; color: var(--text-900); display: flex; align-items: center; gap: 8px; } .cart-head h3 i { color: var(--accent); } .cart-close-btn { width: 34px; height: 34px; border-radius: var(--radius-sm); background: var(--bg-input); border: 1.5px solid var(--border); color: var(--text-500); font-size: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; } .cart-close-btn:hover { background: var(--accent-soft); color: var(--accent); } .cart-body { flex: 1; overflow-y: auto; padding: 16px 22px; } .cart-body::-webkit-scrollbar { width: 4px; } .cart-body::-webkit-scrollbar-track { background: transparent; } .cart-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } .cart-empty { text-align: center; padding: 60px 0; color: var(--text-300); } .cart-empty i { font-size: 44px; display: block; margin-bottom: 14px; opacity: 0.4; } .cart-empty p { font-size: 13px; } .cart-item { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; border-bottom: 1px solid var(--border); } .cart-item:last-child { border-bottom: none; } .cart-item-info { flex: 1; min-width: 0; } .cart-item-name { font-size: 13px; font-weight: 600; color: var(--text-900); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .cart-item-sub { font-size: 11px; color: var(--text-500); margin-top: 3.5px; display: flex; align-items: center; gap: 6px; } .cart-item-size { background: var(--accent-soft); color: var(--accent); padding: 1.5px 5px; border-radius: 4px; font-weight: 700; font-size: 9.5px; font-family: 'Outfit', sans-serif; } .cart-item-price { font-family: 'Outfit', sans-serif; font-weight: 700; font-size: 14px; color: var(--accent); white-space: nowrap; flex-shrink: 0; } .cart-item-right { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; flex-shrink: 0; } .cart-remove-btn { background: none; border: none; color: var(--text-300); cursor: pointer; transition: color 0.2s, transform 0.1s; padding: 4px; display: flex; align-items: center; justify-content: center; font-size: 13px; } .cart-remove-btn:hover { color: #f43f5e; transform: scale(1.15); } .cart-remove-btn:active { transform: scale(0.95); } .cart-foot { padding: 18px 22px; border-top: 1px solid var(--border); } .cart-total-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; } .cart-total-label { font-size: 13px; font-weight: 600; color: var(--text-700); } .cart-total-num { font-family: 'Outfit', sans-serif; font-size: 22px; font-weight: 800; color: var(--text-900); } .checkout-btn { width: 100%; height: 46px; background: linear-gradient(135deg, var(--accent), #6366f1); color: #fff; border: none; border-radius: var(--radius-md); font-size: 14px; font-weight: 700; font-family: inherit; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: opacity 0.2s, transform 0.15s, box-shadow 0.2s; box-shadow: 0 4px 16px var(--accent-glow); } .checkout-btn:hover { opacity: 0.9; transform: translateY(-1px); box-shadow: 0 8px 24px var(--accent-glow); } /* ===== Responsive ===== */ @media (max-width: 900px) { .page-wrap { grid-template-columns: 1fr; } .sidebar { position: static; display: none; } .product-grid { grid-template-columns: repeat(3, 1fr); } } @media (max-width: 768px) { .navbar-inner { padding: 0 16px; } .logo-text { display: none; } .sell-btn span { display: none; } .sell-btn { padding: 0 12px; } .login-btn span { display: none; } .login-btn { padding: 0 12px; } } @media (max-width: 640px) { .product-grid { grid-template-columns: repeat(2, 1fr); } .hero h1 { font-size: 26px; } .hero-stats { display: none; } .cart-panel { width: 100%; } } /* ===== Scrollbar (Global) ===== */ ::-webkit-scrollbar { width: 6px; } ::-webkit-scrollbar-track { background: transparent; } ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 10px; } /* ===== PROFILE MODAL ===== */ .profile-modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.55); backdrop-filter: blur(6px); z-index: 9000; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity 0.3s; } .profile-modal-overlay.show { opacity: 1; pointer-events: all; } .profile-modal-box { background: var(--bg-card); border-radius: var(--radius-xl); width: 100%; max-width: 520px; box-shadow: var(--shadow-lg); overflow: hidden; transform: translateY(20px) scale(0.97); transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1); } .profile-modal-overlay.show .profile-modal-box { transform: translateY(0) scale(1); } .profile-modal-banner { height: 120px; background: linear-gradient(135deg, #0f172a 0%, #1e3a8a 45%, #6366f1 100%); position: relative; } .profile-modal-banner-close { position: absolute; top: 14px; right: 14px; width: 32px; height: 32px; border-radius: 50%; background: rgba(255, 255, 255, 0.15); border: none; color: #fff; font-size: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: background 0.2s; } .profile-modal-banner-close:hover { background: rgba(255, 255, 255, 0.3); } .profile-avatar-wrap { position: absolute; bottom: -40px; left: 50%; transform: translateX(-50%); } .profile-avatar-ring { width: 84px; height: 84px; border-radius: 50%; background: linear-gradient(135deg, var(--accent), #6366f1); padding: 3px; box-shadow: 0 8px 24px var(--accent-glow); } .profile-avatar-ring img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; border: 3px solid var(--bg-card); display: block; } .profile-modal-body { padding: 52px 28px 28px; } .profile-name-row { text-align: center; margin-bottom: 24px; } .profile-username { font-family: 'Outfit', sans-serif; font-size: 20px; font-weight: 800; color: var(--text-900); letter-spacing: -0.5px; } .profile-email { font-size: 13px; color: var(--text-500); margin-top: 4px; } .profile-badge { display: inline-flex; align-items: center; gap: 5px; background: var(--accent-soft); color: var(--accent); border: 1px solid var(--border-accent); border-radius: 100px; padding: 3px 10px; font-size: 11px; font-weight: 700; margin-top: 8px; } .profile-stats-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-bottom: 24px; } .profile-stat-card { background: var(--bg-page); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 14px 10px; text-align: center; } .profile-stat-num { font-family: 'Outfit', sans-serif; font-size: 22px; font-weight: 800; color: var(--accent); } .profile-stat-label { font-size: 11px; color: var(--text-500); margin-top: 2px; } .profile-edit-section { border-top: 1px solid var(--border); padding-top: 20px; } .profile-edit-title { font-size: 13px; font-weight: 700; color: var(--text-700); margin-bottom: 14px; text-transform: uppercase; letter-spacing: 0.5px; } .profile-field { display: flex; flex-direction: column; gap: 5px; margin-bottom: 14px; } .profile-field label { font-size: 12px; font-weight: 600; color: var(--text-700); } .profile-field input { background: var(--bg-input); border: 1.5px solid var(--border); border-radius: var(--radius-sm); padding: 9px 14px; font-size: 13px; font-family: inherit; color: var(--text-900); outline: none; transition: border-color 0.2s, box-shadow 0.2s; } .profile-field input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); } .profile-save-btn { width: 100%; height: 44px; background: linear-gradient(135deg, var(--accent), #6366f1); color: #fff; border: none; border-radius: var(--radius-md); font-size: 14px; font-weight: 700; font-family: inherit; cursor: pointer; transition: all 0.2s; box-shadow: var(--shadow-glow); margin-top: 8px; } .profile-save-btn:hover { transform: translateY(-1.5px); box-shadow: 0 10px 32px var(--accent-glow); } /* ===== ORDERS MODAL ===== */ .orders-modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.55); backdrop-filter: blur(6px); z-index: 9000; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity 0.3s; } .orders-modal-overlay.show { opacity: 1; pointer-events: all; } .orders-modal-box { background: var(--bg-card); border-radius: var(--radius-xl); width: 100%; max-width: 560px; max-height: 85vh; display: flex; flex-direction: column; box-shadow: var(--shadow-lg); overflow: hidden; transform: translateY(20px) scale(0.97); transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1); } .orders-modal-overlay.show .orders-modal-box { transform: translateY(0) scale(1); } .orders-modal-head { padding: 22px 24px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; background: linear-gradient(135deg, var(--bg-card), var(--accent-soft)); } .orders-modal-head h3 { font-size: 17px; font-weight: 800; color: var(--text-900); display: flex; align-items: center; gap: 10px; } .orders-modal-head h3 i { color: var(--accent); font-size: 16px; } .orders-modal-close { width: 32px; height: 32px; border-radius: 50%; background: var(--bg-page); border: 1px solid var(--border); color: var(--text-700); font-size: 13px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; } .orders-modal-close:hover { background: var(--accent-soft); color: var(--accent); } .orders-modal-body { overflow-y: auto; padding: 20px 24px; flex: 1; } .orders-tabs { display: flex; gap: 6px; margin-bottom: 18px; background: var(--bg-page); padding: 4px; border-radius: var(--radius-md); } .orders-tab-btn { flex: 1; padding: 8px 12px; border: none; border-radius: var(--radius-sm); background: transparent; font-size: 12px; font-weight: 600; font-family: inherit; color: var(--text-500); cursor: pointer; transition: all 0.2s; } .orders-tab-btn.active { background: var(--bg-card); color: var(--accent); box-shadow: var(--shadow-sm); } .order-card { background: var(--bg-page); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 16px; margin-bottom: 12px; transition: box-shadow 0.2s, border-color 0.2s; } .order-card:hover { box-shadow: var(--shadow-md); border-color: var(--border-accent); } .order-card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; } .order-id { font-size: 12px; font-weight: 700; color: var(--text-700); font-family: 'Outfit', sans-serif; } .order-status-badge { padding: 3px 10px; border-radius: 100px; font-size: 11px; font-weight: 700; } .order-status-badge.delivered { background: rgba(16, 185, 129, 0.12); color: #10b981; } .order-status-badge.shipping { background: rgba(245, 158, 11, 0.12); color: #f59e0b; } .order-status-badge.pending { background: rgba(100, 116, 139, 0.12); color: var(--text-500); } .order-items-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; } .order-item-row { display: flex; align-items: center; gap: 12px; } .order-item-img { width: 48px; height: 48px; border-radius: var(--radius-sm); object-fit: cover; border: 1px solid var(--border); flex-shrink: 0; } .order-item-info { flex: 1; min-width: 0; } .order-item-name { font-size: 12.5px; font-weight: 600; color: var(--text-900); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .order-item-sub { font-size: 11px; color: var(--text-500); margin-top: 2px; } .order-item-price { font-size: 13px; font-weight: 700; color: var(--accent); font-family: 'Outfit', sans-serif; white-space: nowrap; } .order-card-foot { border-top: 1px solid var(--border); padding-top: 10px; display: flex; align-items: center; justify-content: space-between; } .order-date { font-size: 11px; color: var(--text-500); } .order-total { font-size: 14px; font-weight: 800; color: var(--text-900); font-family: 'Outfit', sans-serif; } .orders-empty { text-align: center; padding: 48px 20px; color: var(--text-500); } .orders-empty i { font-size: 40px; opacity: 0.3; margin-bottom: 12px; display: block; } .orders-empty p { font-size: 14px; } /* ===== TRACKING MODAL ===== */ .tracking-modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); backdrop-filter: blur(6px); z-index: 9000; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity 0.3s; padding: 16px; } .tracking-modal-overlay.show { opacity: 1; pointer-events: all; } .tracking-modal-box { background: var(--bg-card); border-radius: var(--radius-xl); width: 100%; max-width: 620px; max-height: 90vh; display: flex; flex-direction: column; box-shadow: var(--shadow-lg); overflow: hidden; transform: translateY(20px) scale(0.97); transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1); border: 1px solid var(--border); } .tracking-modal-overlay.show .tracking-modal-box { transform: translateY(0) scale(1); } .tracking-modal-head { padding: 20px 24px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; background: linear-gradient(135deg, var(--bg-card), var(--accent-soft)); } .tracking-modal-head h3 { font-size: 17px; font-weight: 800; color: var(--text-900); display: flex; align-items: center; gap: 10px; } .tracking-modal-head h3 i { color: var(--accent); font-size: 18px; } .tracking-modal-close { width: 32px; height: 32px; border-radius: 50%; background: var(--bg-page); border: 1px solid var(--border); color: var(--text-700); font-size: 13px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; } .tracking-modal-close:hover { background: var(--accent-soft); color: var(--accent); } .tracking-modal-body { overflow-y: auto; padding: 22px 24px; display: flex; flex-direction: column; gap: 18px; } .tracking-search-bar { display: flex; gap: 8px; } .tracking-search-input { flex: 1; height: 42px; border: 1.5px solid var(--border); border-radius: var(--radius-md); padding: 0 16px; font-size: 13px; font-family: inherit; background: var(--bg-input); color: var(--text-900); outline: none; transition: border-color 0.2s, box-shadow 0.2s; } .tracking-search-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); } .tracking-search-btn { height: 42px; padding: 0 18px; border-radius: var(--radius-md); background: linear-gradient(135deg, var(--accent) 0%, #6366f1 100%); border: none; color: #fff; font-size: 13px; font-weight: 600; font-family: inherit; cursor: pointer; transition: opacity 0.2s, transform 0.15s; display: flex; align-items: center; gap: 6px; white-space: nowrap; } .tracking-search-btn:hover { opacity: 0.92; transform: translateY(-1px); } .track-quick-orders { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: -6px; } .track-quick-label { font-size: 11.5px; color: var(--text-500); font-weight: 500; } .track-chip { background: var(--bg-page); border: 1px solid var(--border); border-radius: 100px; padding: 4px 12px; font-size: 11px; font-weight: 600; font-family: 'Outfit', sans-serif; color: var(--text-700); cursor: pointer; transition: all 0.2s; display: inline-flex; align-items: center; gap: 5px; } .track-chip:hover, .track-chip.active { background: var(--accent-soft); border-color: var(--border-accent); color: var(--accent); } /* Tracking Main Card */ .track-card { background: var(--bg-page); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: 18px 20px; display: flex; flex-direction: column; gap: 16px; } .track-card-top { display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 12px; padding-bottom: 14px; border-bottom: 1px dashed var(--border); } .track-courier-info { display: flex; align-items: center; gap: 10px; } .track-courier-logo { width: 42px; height: 42px; border-radius: var(--radius-sm); background: linear-gradient(135deg, #f59e0b, #ef4444); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 18px; box-shadow: 0 4px 12px rgba(245, 158, 11, 0.25); } .track-courier-name { font-weight: 700; font-size: 14px; color: var(--text-900); } .track-num-wrap { display: flex; align-items: center; gap: 6px; margin-top: 3px; } .track-num { font-size: 12px; color: var(--text-500); font-family: 'Outfit', monospace; letter-spacing: 0.5px; font-weight: 600; } .copy-track-btn { background: transparent; border: none; color: var(--accent); font-size: 11px; cursor: pointer; padding: 2px 6px; border-radius: 4px; transition: background 0.2s; } .copy-track-btn:hover { background: var(--accent-soft); } .track-eta-wrap { text-align: right; } .track-eta-label { font-size: 11px; color: var(--text-500); } .track-eta-val { font-size: 13px; font-weight: 700; color: var(--accent); margin-top: 2px; } /* Stepper */ .track-stepper { display: flex; justify-content: space-between; position: relative; margin: 12px 0 6px; } .track-stepper-line { position: absolute; top: 17px; left: 30px; right: 30px; height: 3px; background: var(--border); z-index: 1; } .track-stepper-progress { position: absolute; top: 17px; left: 30px; height: 3px; background: linear-gradient(90deg, var(--accent), #10b981); z-index: 1; transition: width 0.4s ease; } .track-step-item { display: flex; flex-direction: column; align-items: center; position: relative; z-index: 2; flex: 1; text-align: center; } .track-step-icon { width: 36px; height: 36px; border-radius: 50%; background: var(--bg-card); border: 2px solid var(--border); color: var(--text-300); display: flex; align-items: center; justify-content: center; font-size: 13px; transition: all 0.3s; } .track-step-item.completed .track-step-icon { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 0 0 4px var(--accent-soft); } .track-step-item.current .track-step-icon { background: #10b981; border-color: #10b981; color: #fff; box-shadow: 0 0 0 5px rgba(16, 185, 129, 0.25); animation: pulse-ring 2s infinite; } .track-step-title { font-size: 11px; font-weight: 600; color: var(--text-500); margin-top: 8px; white-space: nowrap; } .track-step-item.completed .track-step-title, .track-step-item.current .track-step-title { color: var(--text-900); font-weight: 700; } /* Timeline list */ .track-timeline-wrap { background: var(--bg-card); border-radius: var(--radius-md); border: 1px solid var(--border); padding: 16px 18px; } .track-timeline-title { font-size: 12px; font-weight: 700; color: var(--text-700); margin-bottom: 14px; display: flex; align-items: center; gap: 6px; } .track-timeline-list { position: relative; padding-left: 20px; border-left: 2px solid var(--border); display: flex; flex-direction: column; gap: 16px; margin-left: 8px; } .track-timeline-node { position: relative; } .track-timeline-dot { position: absolute; left: -27px; top: 3px; width: 12px; height: 12px; border-radius: 50%; background: var(--border); border: 2px solid var(--bg-card); } .track-timeline-node.active .track-timeline-dot { background: #10b981; box-shadow: 0 0 0 4px rgba(16, 185, 129, 0.25); } .track-timeline-desc { font-size: 12.5px; color: var(--text-900); font-weight: 600; line-height: 1.4; } .track-timeline-time { font-size: 11px; color: var(--text-500); margin-top: 3px; display: flex; align-items: center; gap: 6px; } .track-timeline-location { display: inline-block; background: var(--bg-page); border-radius: 4px; padding: 1px 6px; font-size: 10.5px; color: var(--text-700); } /* Driver / Actions card */ .track-driver-box { display: flex; align-items: center; justify-content: space-between; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 12px 16px; gap: 12px; flex-wrap: wrap; } .track-driver-left { display: flex; align-items: center; gap: 10px; } .track-driver-avatar { width: 38px; height: 38px; border-radius: 50%; background: var(--accent-soft); color: var(--accent); display: flex; align-items: center; justify-content: center; font-size: 16px; } .track-driver-name { font-size: 12.5px; font-weight: 700; color: var(--text-900); } .track-driver-meta { font-size: 11px; color: var(--text-500); } .track-actions-bar { display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-wrap: wrap; } .track-action-btn { height: 36px; padding: 0 16px; border-radius: var(--radius-sm); font-size: 12px; font-weight: 600; font-family: inherit; cursor: pointer; transition: all 0.2s; display: inline-flex; align-items: center; gap: 6px; border: 1.5px solid var(--border); background: var(--bg-card); color: var(--text-700); } .track-action-btn:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); } .track-action-btn.primary { background: linear-gradient(135deg, #10b981 0%, #059669 100%); border: none; color: #fff; box-shadow: 0 3px 10px rgba(16, 185, 129, 0.25); } .track-action-btn.primary:hover { opacity: 0.9; transform: translateY(-1px); } .order-track-btn { height: 30px; padding: 0 12px; border-radius: var(--radius-sm); font-size: 11.5px; font-weight: 600; font-family: inherit; background: var(--accent-soft); border: 1px solid var(--border-accent); color: var(--accent); cursor: pointer; display: inline-flex; align-items: center; gap: 5px; transition: all 0.2s; } .order-track-btn:hover { background: var(--accent); color: #fff; box-shadow: 0 3px 10px var(--accent-glow); } /* ===== PAYMENT & CHECKOUT MODAL ===== */ .payment-modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.65); backdrop-filter: blur(8px); z-index: 9500; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity 0.3s; padding: 16px; } .payment-modal-overlay.show { opacity: 1; pointer-events: all; } .payment-modal-box { background: var(--bg-card); border-radius: var(--radius-xl); width: 100%; max-width: 680px; max-height: 92vh; display: flex; flex-direction: column; box-shadow: var(--shadow-lg); overflow: hidden; transform: translateY(20px) scale(0.97); transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1); border: 1px solid var(--border); } .payment-modal-overlay.show .payment-modal-box { transform: translateY(0) scale(1); } .payment-modal-head { padding: 20px 24px; border-bottom: 1px solid var(--border); display: flex; align-items: center; justify-content: space-between; background: linear-gradient(135deg, var(--bg-card), var(--accent-soft)); } .payment-modal-head h3 { font-size: 17px; font-weight: 800; color: var(--text-900); display: flex; align-items: center; gap: 10px; } .payment-modal-head h3 i { color: #10b981; font-size: 18px; } .payment-modal-close { width: 32px; height: 32px; border-radius: 50%; background: var(--bg-page); border: 1px solid var(--border); color: var(--text-700); font-size: 13px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; } .payment-modal-close:hover { background: var(--accent-soft); color: var(--accent); } .payment-modal-body { overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 20px; } .payment-section-title { font-size: 13px; font-weight: 700; color: var(--text-900); display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; } .payment-section-title span { display: flex; align-items: center; gap: 7px; } .payment-section-title i { color: var(--accent); font-size: 14px; } /* Delivery Address Box */ .payment-address-card { background: var(--bg-page); border: 1.5px solid var(--border); border-radius: var(--radius-md); padding: 14px 18px; display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; transition: border-color 0.2s; } .payment-address-card:hover { border-color: var(--border-accent); } .addr-name { font-weight: 700; font-size: 13px; color: var(--text-900); display: flex; align-items: center; gap: 8px; } .addr-phone { font-size: 12px; color: var(--text-500); font-weight: 500; } .addr-detail { font-size: 12.5px; color: var(--text-700); margin-top: 4px; line-height: 1.5; } .addr-edit-btn { background: var(--accent-soft); border: 1px solid var(--border-accent); color: var(--accent); padding: 4px 10px; border-radius: var(--radius-sm); font-size: 11.5px; font-weight: 600; cursor: pointer; transition: all 0.2s; white-space: nowrap; } .addr-edit-btn:hover { background: var(--accent); color: #fff; } /* Shipping Option Grid */ .shipping-options-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 8px; } .shipping-card { border: 1.5px solid var(--border); background: var(--bg-card); border-radius: var(--radius-md); padding: 12px 14px; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; gap: 10px; } .shipping-card.active { border-color: var(--accent); background: var(--accent-soft); box-shadow: 0 2px 10px var(--accent-glow); } .shipping-card input[type="radio"] { accent-color: var(--accent); } .shipping-title { font-size: 12px; font-weight: 700; color: var(--text-900); } .shipping-sub { font-size: 11px; color: var(--text-500); margin-top: 2px; } /* Payment Methods Grid */ .payment-methods-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } @media (max-width: 600px) { .payment-methods-grid { grid-template-columns: repeat(2, 1fr); } } .pay-method-tab { background: var(--bg-page); border: 1.5px solid var(--border); border-radius: var(--radius-md); padding: 12px 8px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; cursor: pointer; transition: all 0.2s; text-align: center; position: relative; } .pay-method-tab:hover { border-color: var(--border-accent); background: var(--bg-card); } .pay-method-tab.active { border-color: var(--accent); background: var(--accent-soft); color: var(--accent); box-shadow: 0 4px 14px var(--accent-glow); } .pay-method-tab.active::after { content: '✓'; position: absolute; top: 4px; right: 6px; font-size: 10px; font-weight: 800; color: var(--accent); } .pay-method-icon { font-size: 20px; color: var(--accent); } .pay-method-title { font-size: 11.5px; font-weight: 700; color: var(--text-900); } .pay-method-tab.active .pay-method-title { color: var(--accent); } /* Payment Content Containers */ .pay-content-box { background: var(--bg-page); border: 1.5px solid var(--border); border-radius: var(--radius-lg); padding: 18px 20px; display: flex; flex-direction: column; gap: 14px; } /* QR Code Box */ .qr-wrapper { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; justify-content: center; } .qr-card { background: #ffffff; padding: 14px; border-radius: var(--radius-md); box-shadow: var(--shadow-sm); text-align: center; border: 1px solid rgba(0, 0, 0, 0.08); } .qr-img { width: 150px; height: 150px; object-fit: contain; display: block; margin: 0 auto; border-radius: 4px; } .qr-info-right { flex: 1; min-width: 220px; display: flex; flex-direction: column; gap: 10px; } .qr-promptpay-badge { display: inline-flex; align-items: center; gap: 6px; background: #003764; color: #fff; padding: 4px 12px; border-radius: 6px; font-size: 11px; font-weight: 700; width: fit-content; } .qr-timer-box { background: rgba(239, 68, 68, 0.08); border: 1px solid rgba(239, 68, 68, 0.2); border-radius: var(--radius-sm); padding: 8px 12px; font-size: 12px; color: #ef4444; display: flex; align-items: center; gap: 6px; font-weight: 600; } /* Credit Card Preview */ .card-preview-box { background: linear-gradient(135deg, #1e293b 0%, #0f172a 50%, #1e1b4b 100%); border-radius: var(--radius-lg); padding: 20px 24px; color: #fff; position: relative; overflow: hidden; box-shadow: 0 10px 25px rgba(0, 0, 0, 0.25); max-width: 380px; margin: 0 auto 10px; } .card-preview-box::before { content: ''; position: absolute; top: -40px; right: -40px; width: 140px; height: 140px; background: radial-gradient(circle, rgba(99, 102, 241, 0.4), transparent 70%); border-radius: 50%; } .card-preview-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; } .card-sim-chip { width: 36px; height: 26px; background: linear-gradient(135deg, #fcd34d, #f59e0b); border-radius: 4px; } .card-brand-logo { font-family: 'Outfit', sans-serif; font-size: 18px; font-weight: 900; letter-spacing: 1px; color: #93c5fd; } .card-number-display { font-family: 'Courier New', monospace; font-size: 18px; letter-spacing: 3px; font-weight: 700; margin-bottom: 18px; text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); } .card-preview-bottom { display: flex; justify-content: space-between; align-items: flex-end; font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: rgba(255, 255, 255, 0.7); } .card-holder-val, .card-expiry-val { font-weight: 700; color: #fff; font-size: 13px; margin-top: 2px; font-family: 'Outfit', sans-serif; } /* Card form fields */ .card-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } .card-form-field { display: flex; flex-direction: column; gap: 5px; } .card-form-field.full { grid-column: span 2; } .card-form-field label { font-size: 11.5px; font-weight: 600; color: var(--text-700); } .card-form-field input { height: 38px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); padding: 0 12px; font-size: 13px; font-family: inherit; background: var(--bg-card); color: var(--text-900); outline: none; transition: border-color 0.2s, box-shadow 0.2s; } .card-form-field input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); } /* Bank Select Grid */ .bank-select-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; } .bank-option-card { background: var(--bg-card); border: 1.5px solid var(--border); border-radius: var(--radius-md); padding: 12px 14px; cursor: pointer; transition: all 0.2s; display: flex; align-items: center; gap: 10px; } .bank-option-card.active { border-color: var(--accent); background: var(--accent-soft); } .bank-badge { width: 34px; height: 34px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 11px; color: #fff; font-family: 'Outfit', sans-serif; flex-shrink: 0; } .bank-kbank { background: #138f2d; } .bank-scb { background: #4e2e7f; } .bank-bbl { background: #1e3a8a; } .bank-ktb { background: #00a4e4; } .bank-name { font-size: 12px; font-weight: 700; color: var(--text-900); } .bank-acc { font-size: 11px; color: var(--text-500); font-family: monospace; } /* Coupon section */ .coupon-wrapper { display: flex; gap: 8px; } .coupon-input { flex: 1; height: 38px; border: 1.5px solid var(--border); border-radius: var(--radius-sm); padding: 0 14px; font-size: 12.5px; font-family: inherit; background: var(--bg-card); color: var(--text-900); outline: none; text-transform: uppercase; } .coupon-input:focus { border-color: var(--accent); } .coupon-apply-btn { height: 38px; padding: 0 16px; border-radius: var(--radius-sm); background: var(--bg-card); border: 1.5px solid var(--border); color: var(--text-700); font-weight: 600; font-size: 12px; cursor: pointer; transition: all 0.2s; } .coupon-apply-btn:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); } .coupon-tags-row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 6px; } .coupon-tag { font-size: 11px; background: rgba(16, 185, 129, 0.1); color: #10b981; border: 1px dashed rgba(16, 185, 129, 0.35); padding: 2px 8px; border-radius: 4px; cursor: pointer; transition: all 0.2s; } .coupon-tag:hover { background: rgba(16, 185, 129, 0.2); } /* Order Summary Table */ .order-summary-card { background: var(--bg-page); border: 1px solid var(--border); border-radius: var(--radius-md); padding: 16px 18px; display: flex; flex-direction: column; gap: 10px; } .summary-line { display: flex; justify-content: space-between; font-size: 12.5px; color: var(--text-700); } .summary-line.total { border-top: 1.5px dashed var(--border); padding-top: 12px; margin-top: 4px; font-size: 15px; font-weight: 800; color: var(--text-900); } .summary-line.total .total-amount { color: var(--accent); font-size: 18px; font-family: 'Outfit', sans-serif; } .pay-confirm-btn { width: 100%; height: 48px; background: linear-gradient(135deg, #10b981 0%, #059669 100%); color: #fff; border: none; border-radius: var(--radius-md); font-size: 15px; font-weight: 700; font-family: inherit; cursor: pointer; transition: all 0.2s; box-shadow: 0 4px 16px rgba(16, 185, 129, 0.35); display: flex; align-items: center; justify-content: center; gap: 8px; } .pay-confirm-btn:hover { transform: translateY(-1.5px); box-shadow: 0 6px 20px rgba(16, 185, 129, 0.45); } .pay-confirm-btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; } </style> </head> <body> <!-- ===== NAVBAR ===== --> <nav class="navbar"> <div class="navbar-inner"> <a href="#" class="logo"> <div class="logo-mark">H</div> <span class="logo-text">ho<span>ru</span></span> </a> <div class="nav-search"> <input type="text" id="search-input" placeholder="ค้นหาสินค้า แบรนด์ หรือสไตล์..." oninput="handleSearch()"> <button class="nav-search-btn" aria-label="ค้นหา"> <i class="fa-solid fa-magnifying-glass"></i> </button> </div> <div class="nav-actions"> <button class="theme-pill" onclick="toggleTheme()" id="theme-btn" title="เปลี่ยนธีม"> <i class="fa-solid fa-sun theme-label-light"></i> <i class="fa-solid fa-moon theme-label-dark"></i> <span class="theme-label-light">สว่าง</span> <span class="theme-label-dark">มืด</span> </button> <button class="nav-icon-btn" onclick="if(!currentUser){showToast('🔒 กรุณาเข้าสู่ระบบก่อนติดตามพัสดุครับ');openLoginModal();}else{openTrackingModal();}" title="ติดตามสถานะพัสดุ"> <i class="fa-solid fa-truck-fast"></i> </button> <button class="nav-icon-btn" onclick="if(!currentUser){showToast('🔒 กรุณาเข้าสู่ระบบก่อนดูรายการโปรดครับ');openLoginModal();}else{showToast('❤️ คุณมีสินค้าในรายการโปรด');}" title="รายการโปรด"> <i class="fa-regular fa-heart"></i> </button> <button class="nav-icon-btn" id="cart-trigger" onclick="toggleCart()" title="ตะกร้าสินค้า"> <i class="fa-solid fa-bag-shopping"></i> <span class="nav-badge" id="cart-count">0</span> </button> <a href="admin.html" target="_blank" class="nav-icon-btn" title="เข้าสู่ระบบเฉพาะ Admin" style="color: var(--accent); text-decoration: none; display: flex; align-items: center; justify-content: center;"> <i class="fa-solid fa-shield-halved"></i> </a> <!-- User profile/login element dynamically loaded --> <div id="user-navbar-section"></div> </div> </div> </nav> <!-- ===== PAGE BODY ===== --> <div class="page-wrap"> <!-- MAIN --> <section class="main-section"> <!-- Product Grid --> <div class="product-grid" id="product-grid-container"> <!-- Products will be generated here dynamically by javascript --> </div> </section> </div> <!-- ===== ADD PRODUCT MODAL ===== --> <div class="modal-overlay" id="sell-modal" onclick="closeSellModal(event)"> <div class="modal-box" onclick="event.stopPropagation()"> <div class="modal-header"> <h3><i class="fa-solid fa-plus"></i> ลงขายสินค้าใหม่ของคุณ</h3> <button class="modal-close-btn" onclick="closeSellModal()"><i class="fa-solid fa-xmark"></i></button> </div> <form id="add-product-form" onsubmit="saveProduct(event)"> <div class="modal-body"> <div class="form-group"> <label for="p-name">ชื่อสินค้า <span style="color:red">*</span></label> <input type="text" id="p-name" placeholder="ระบุชื่อสินค้า เช่น เสื้อยืดพิมพ์ลายกราฟิก" required> </div> <div class="form-row"> <div class="form-group"> <label for="p-brand">แบรนด์ <span style="color:red">*</span></label> <input type="text" id="p-brand" placeholder="เช่น UNIQLO, ZARA, Local" required> </div> <div class="form-group"> <label for="p-badge">ป้ายกำกับสินค้า</label> <select id="p-badge"> <option value="">ไม่มี</option> <option value="Official">Official</option> <option value="Hot">Hot</option> </select> </div> </div> <div class="form-row"> <div class="form-group"> <label for="p-price">ราคาขาย (฿) <span style="color:red">*</span></label> <input type="number" id="p-price" placeholder="เช่น 290" min="0" required> </div> <div class="form-group"> <label for="p-original">ราคาเต็มดั้งเดิม (฿)</label> <input type="number" id="p-original" placeholder="เช่น 450" min="0"> </div> </div> <div class="form-group"> <label for="p-image">ลิงก์ URL รูปภาพสินค้า <span style="color:red">*</span></label> <input type="url" id="p-image" placeholder="https://images.unsplash.com/..." required> <small style="color: var(--text-500); font-size:11px;">สามารถใช้รูปตัวอย่างจาก Unsplash หรือ URL อื่น ๆ ได้</small> </div> <div class="form-row"> <div class="form-group"> <label for="p-rating">คะแนนเริ่มต้น</label> <input type="number" id="p-rating" placeholder="5.0" min="1" max="5" step="0.1" value="5.0"> </div> <div class="form-group"> <label for="p-location">จัดส่งจาก <span style="color:red">*</span></label> <select id="p-location" required> <option value="ในประเทศ">ในประเทศ</option> <option value="ต่างประเทศ">ต่างประเทศ</option> </select> </div> </div> <div class="form-group"> <label>ไซส์เสื้อที่มีให้เลือก</label> <div class="checkbox-group-wrapper"> <div class="checkbox-group"> <input type="checkbox" id="size-s" value="S" checked> <label for="size-s">S</label> </div> <div class="checkbox-group"> <input type="checkbox" id="size-m" value="M" checked> <label for="size-m">M</label> </div> <div class="checkbox-group"> <input type="checkbox" id="size-l" value="L" checked> <label for="size-l">L</label> </div> <div class="checkbox-group"> <input type="checkbox" id="size-xl" value="XL" checked> <label for="size-xl">XL</label> </div> </div> </div> </div> <div class="modal-footer"> <button type="button" class="btn-cancel" onclick="closeSellModal()">ยกเลิก</button> <button type="submit" class="btn-submit">ลงขายสินค้า</button> </div> </form> </div> </div> <!-- ===== LOGIN / SIGNUP MODAL ===== --> <div class="modal-overlay" id="login-modal" onclick="closeLoginModal(event)"> <div class="modal-box" onclick="event.stopPropagation()" style="max-width: 400px;"> <div class="modal-header"> <h3 id="auth-modal-title"><i class="fa-solid fa-lock" style="color: var(--accent); margin-right: 6px;"></i>เข้าสู่ระบบ HORU</h3> <button class="modal-close-btn" onclick="closeLoginModal()"><i class="fa-solid fa-xmark"></i></button> </div> <!-- Login Form --> <form id="login-form" onsubmit="handleLoginSubmit(event)"> <div class="modal-body" style="gap: 16px;"> <div class="form-group"> <label for="login-email">อีเมล / ชื่อผู้ใช้</label> <input type="text" id="login-email" placeholder="ระบุอีเมล เช่น user@horu.com" required> </div> <div class="form-group"> <label for="login-password">รหัสผ่าน</label> <input type="password" id="login-password" placeholder="ระบุรหัสผ่าน" required> </div> <div style="font-size: 12px; color: var(--text-700); text-align: right; margin-top: -6px;"> ยังไม่มีบัญชี? <a href="#" onclick="toggleAuthMode('signup')" style="color: var(--accent); font-weight: 600; text-decoration: none;">สมัครสมาชิกใหม่</a> </div> <div style="margin-top: 4px; padding: 12px; border-radius: var(--radius-sm); background: var(--accent-soft); border: 1px solid var(--border-accent); font-size: 11.5px; color: var(--accent-dark); line-height: 1.5;"> <i class="fa-solid fa-circle-info"></i> <strong>สำหรับทดสอบ:</strong> สามารถกรอกบัญชีที่สมัครใหม่ หรือใช้ <br> อีเมล: <code>user@horu.com</code> รหัสผ่าน: <code>password123</code> </div> <div style="font-size: 12px; text-align: center; border-top: 1px dashed var(--border); padding-top: 10px; margin-top: 6px;"> <a href="admin.html" target="_blank" style="color: var(--accent); font-weight: 600; text-decoration: none; display: inline-flex; align-items: center; gap: 6px;"> <i class="fa-solid fa-shield-halved"></i> เข้าสู่ระบบเฉพาะผู้ดูแลระบบ (Admin) </a> </div> </div> <div class="modal-footer" style="padding-top: 10px;"> <button type="button" class="btn-cancel" onclick="closeLoginModal()">ยกเลิก</button> <button type="submit" class="btn-submit"><i class="fa-solid fa-arrow-right-to-bracket"></i> เข้าสู่ระบบ</button> </div> </form> <!-- Signup Form (Hidden by Default) --> <form id="signup-form" onsubmit="handleSignupSubmit(event)" style="display: none;"> <div class="modal-body" style="gap: 16px;"> <div class="form-group"> <label for="signup-email">อีเมล / ชื่อผู้ใช้ <span style="color: red;">*</span></label> <input type="text" id="signup-email" placeholder="ระบุอีเมล เช่น newuser@horu.com" required> </div> <div class="form-group"> <label for="signup-password">รหัสผ่าน <span style="color: red;">*</span></label> <input type="password" id="signup-password" placeholder="ตั้งรหัสผ่านของคุณ" required> </div> <div class="form-group"> <label for="signup-confirm">ยืนยันรหัสผ่าน <span style="color: red;">*</span></label> <input type="password" id="signup-confirm" placeholder="กรอกรหัสผ่านอีกครั้ง" required> </div> <div style="font-size: 12px; color: var(--text-700); text-align: right; margin-top: -6px;"> มีบัญชีอยู่แล้ว? <a href="#" onclick="toggleAuthMode('login')" style="color: var(--accent); font-weight: 600; text-decoration: none;">เข้าสู่ระบบ</a> </div> </div> <div class="modal-footer" style="padding-top: 10px;"> <button type="button" class="btn-cancel" onclick="closeLoginModal()">ยกเลิก</button> <button type="submit" class="btn-submit"><i class="fa-solid fa-user-plus"></i> สมัครสมาชิก</button> </div> </form> </div> </div> <!-- Cart Overlay --> <div class="cart-overlay" id="cart-overlay" onclick="toggleCart()"></div> <!-- Cart Panel --> <div class="cart-panel" id="cart-panel"> <div class="cart-head"> <h3><i class="fa-solid fa-bag-shopping"></i> ตะกร้าสินค้า</h3> <button class="cart-close-btn" onclick="toggleCart()"><i class="fa-solid fa-xmark"></i></button> </div> <div class="cart-body" id="cart-items"> <div class="cart-empty"> <i class="fa-solid fa-bag-shopping"></i> <p>ยังไม่มีสินค้าในตะกร้า</p> </div> </div> <div class="cart-foot"> <div class="cart-total-row"> <span class="cart-total-label">ยอดรวมทั้งหมด</span> <span class="cart-total-num" id="cart-total">฿0</span> </div> <button class="checkout-btn" onclick="checkout()"> <i class="fa-solid fa-lock"></i> ชำระเงิน </button> </div> </div> <!-- ===== PROFILE MODAL ===== --> <div class="profile-modal-overlay" id="profile-modal" onclick="closeProfileModal(event)"> <div class="profile-modal-box" onclick="event.stopPropagation()"> <div class="profile-modal-banner"> <button class="profile-modal-banner-close" onclick="closeProfileModal()"> <i class="fa-solid fa-xmark"></i> </button> <div class="profile-avatar-wrap"> <div class="profile-avatar-ring"> <img id="profile-avatar-img" src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=200" alt="Avatar"> </div> </div> </div> <div class="profile-modal-body"> <div class="profile-name-row"> <div class="profile-username" id="profile-display-name">—</div> <div class="profile-email" id="profile-display-email">—</div> <div class="profile-badge"><i class="fa-solid fa-crown"></i> สมาชิก HORU</div> </div> <div class="profile-stats-row"> <div class="profile-stat-card"> <div class="profile-stat-num" id="profile-stat-orders">0</div> <div class="profile-stat-label">คำสั่งซื้อ</div> </div> <div class="profile-stat-card"> <div class="profile-stat-num">0</div> <div class="profile-stat-label">รายการโปรด</div> </div> <div class="profile-stat-card"> <div class="profile-stat-num">0</div> <div class="profile-stat-label">รีวิวสินค้า</div> </div> </div> <div class="profile-edit-section"> <div class="profile-edit-title">แก้ไขข้อมูลส่วนตัว</div> <div class="profile-field"> <label for="profile-input-name">ชื่อที่แสดง</label> <input type="text" id="profile-input-name" placeholder="ระบุชื่อที่แสดง"> </div> <div class="profile-field"> <label for="profile-input-phone">เบอร์โทรศัพท์</label> <input type="tel" id="profile-input-phone" placeholder="เช่น 08x-xxx-xxxx"> </div> <div class="profile-field"> <label for="profile-input-address">ที่อยู่จัดส่ง</label> <input type="text" id="profile-input-address" placeholder="บ้านเลขที่ ถนน อำเภอ จังหวัด"> </div> <button class="profile-save-btn" onclick="saveProfileData()"> <i class="fa-solid fa-floppy-disk"></i> บันทึกข้อมูล </button> </div> <!-- Account Usage Logs --> <div class="profile-edit-section" style="margin-top: 14px;"> <div class="profile-edit-title"><i class="fa-solid fa-clock-rotate-left" style="color:var(--accent); margin-right:4px;"></i> ประวัติและบันทึกการใช้งานบัญชีนี้</div> <div style="display:flex; justify-content:space-between; background:var(--bg-page); padding:10px 14px; border-radius:var(--radius-sm); border:1px solid var(--border); font-size:12px; margin-bottom:10px;"> <div>เข้าใช้งานล่าสุด: <b id="profile-last-login" style="color:var(--accent);">—</b></div> <div>เข้าสู่ระบบแล้ว: <b id="profile-login-count" style="color:var(--text-900);">0</b> ครั้ง </div> </div> <div id="profile-activity-list" style="max-height: 150px; overflow-y: auto; font-size: 11.5px; display: flex; flex-direction: column; gap: 6px;"> <!-- Populated by JS --> </div> </div> </div> </div> </div> <!-- ===== ORDERS MODAL ===== --> <div class="orders-modal-overlay" id="orders-modal" onclick="closeOrdersModal(event)"> <div class="orders-modal-box" onclick="event.stopPropagation()"> <div class="orders-modal-head"> <h3><i class="fa-solid fa-clock-rotate-left"></i> รายการสั่งซื้อ</h3> <button class="orders-modal-close" onclick="closeOrdersModal()"> <i class="fa-solid fa-xmark"></i> </button> </div> <div class="orders-modal-body"> <div class="orders-tabs"> <button class="orders-tab-btn active" id="tab-all" onclick="switchOrderTab('all')">ทั้งหมด</button> <button class="orders-tab-btn" id="tab-shipping" onclick="switchOrderTab('shipping')">กำลังจัดส่ง</button> <button class="orders-tab-btn" id="tab-delivered" onclick="switchOrderTab('delivered')">ได้รับแล้ว</button> </div> <div id="orders-list-container"> <!-- dynamically rendered --> </div> </div> </div> </div> <!-- ===== TRACKING MODAL ===== --> <div class="tracking-modal-overlay" id="tracking-modal" onclick="closeTrackingModal(event)"> <div class="tracking-modal-box" onclick="event.stopPropagation()"> <div class="tracking-modal-head"> <h3><i class="fa-solid fa-truck-fast"></i> ติดตามสถานะสินค้า & พัสดุ</h3> <button class="tracking-modal-close" onclick="closeTrackingModal()"> <i class="fa-solid fa-xmark"></i> </button> </div> <div class="tracking-modal-body"> <!-- Search tracking bar --> <div class="tracking-search-bar"> <input type="text" id="track-search-input" class="tracking-search-input" placeholder="กรอกรหัสคำสั่งซื้อ หรือ เลขพัสดุ (เช่น ORU-2026-00912)..." onkeydown="if(event.key==='Enter') searchTracking()"> <button class="tracking-search-btn" onclick="searchTracking()"> <i class="fa-solid fa-magnifying-glass"></i> ค้นหา </button> </div> <!-- Quick chip selectors for active orders --> <div class="track-quick-orders" id="track-quick-chips"> <!-- Populated by JS --> </div> <!-- Main dynamic tracking details container --> <div id="tracking-detail-container"> <!-- Populated by JS --> </div> </div> </div> </div> <!-- ===== PAYMENT / CHECKOUT MODAL ===== --> <div class="payment-modal-overlay" id="payment-modal" onclick="closePaymentModal(event)"> <div class="payment-modal-box" onclick="event.stopPropagation()"> <div class="payment-modal-head"> <h3><i class="fa-solid fa-shield-halved"></i> ชำระเงินอย่างปลอดภัย</h3> <button class="payment-modal-close" onclick="closePaymentModal()"> <i class="fa-solid fa-xmark"></i> </button> </div> <div class="payment-modal-body"> <!-- Section 1: Delivery Address --> <div> <div class="payment-section-title"> <span><i class="fa-solid fa-location-dot"></i> ที่อยู่จัดส่งพัสดุ</span> <button class="addr-edit-btn" onclick="openProfileModal()"><i class="fa-regular fa-pen-to-square"></i> แก้ไข</button> </div> <div class="payment-address-card"> <div style="flex:1;"> <div class="addr-name"> <span id="pay-addr-name">คุณลูกค้า HORU</span> <span class="addr-phone" id="pay-addr-phone">08x-xxx-xxxx</span> </div> <div class="addr-detail" id="pay-addr-detail">99/1 ถ.สุขุมวิท แขวงคลองเตย เขตคลองเตย กรุงเทพมหานคร 10110</div> </div> </div> <!-- Shipping Option --> <div class="shipping-options-grid"> <div class="shipping-card active" id="ship-standard" onclick="selectShipping('standard')"> <input type="radio" name="shipping-speed" id="rad-ship-std" checked> <div> <div class="shipping-title">จัดส่งมาตรฐาน (Standard)</div> <div class="shipping-sub">ฟรีค่าจัดส่ง • 2-3 วันทำการ</div> </div> </div> <div class="shipping-card" id="ship-express" onclick="selectShipping('express')"> <input type="radio" name="shipping-speed" id="rad-ship-exp"> <div> <div class="shipping-title">จัดส่งด่วนพิเศษ (Express +฿45)</div> <div class="shipping-sub">รับสินค้าภายใน 24 ชม.</div> </div> </div> </div> </div> <!-- Section 2: Payment Methods --> <div> <div class="payment-section-title"> <span><i class="fa-solid fa-wallet"></i> เลือกช่องทางชำระเงิน</span> </div> <div class="payment-methods-grid"> <div class="pay-method-tab active" id="tab-pay-promptpay" onclick="switchPayMethod('promptpay')"> <i class="fa-solid fa-qrcode pay-method-icon"></i> <span class="pay-method-title">พร้อมเพย์ QR</span> </div> <div class="pay-method-tab" id="tab-pay-card" onclick="switchPayMethod('card')"> <i class="fa-regular fa-credit-card pay-method-icon"></i> <span class="pay-method-title">บัตรเครดิต/เดบิต</span> </div> <div class="pay-method-tab" id="tab-pay-bank" onclick="switchPayMethod('bank')"> <i class="fa-solid fa-building-columns pay-method-icon"></i> <span class="pay-method-title">โอนผ่านธนาคาร</span> </div> <div class="pay-method-tab" id="tab-pay-cod" onclick="switchPayMethod('cod')"> <i class="fa-solid fa-hand-holding-dollar pay-method-icon"></i> <span class="pay-method-title">เก็บเงินปลายทาง</span> </div> </div> </div> <!-- Section 3: Dynamic Payment Content --> <!-- 1. PromptPay Content --> <div class="pay-content-box" id="pay-content-promptpay"> <div class="qr-wrapper"> <div class="qr-card"> <img class="qr-img" id="promptpay-qr-img" src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=HORU-PAY-PROMPTPAY" alt="QR Code PromptPay"> <div style="font-size:11px; color:#64748b; margin-top:6px; font-weight:600;"> สแกนเพื่อจ่ายเงิน</div> </div> <div class="qr-info-right"> <div class="qr-promptpay-badge"><i class="fa-solid fa-bolt"></i> Thai PromptPay</div> <div style="font-size:13px; font-weight:700; color:var(--text-900);">ยอดชำระ: <span class="total-pay-display" id="qr-pay-amount" style="color:var(--accent); font-size:16px;">฿0</span></div> <div class="qr-timer-box"> <i class="fa-regular fa-clock"></i> QR Code หมดอายุใน: <span id="qr-countdown">14:59</span> นาที </div> <p style="font-size:11.5px; color:var(--text-500); line-height:1.5;"> 1. เปิดแอป Mobile Banking ของธนาคารใดก็ได้<br> 2. สแกน QR Code นี้เพื่อทำการชำระเงิน<br> 3. ระบบจะตรวจสอบยอดเงินอัตโนมัติทันที </p> </div> </div> </div> <!-- 2. Credit Card Content --> <div class="pay-content-box" id="pay-content-card" style="display:none;"> <!-- Card Visual Preview --> <div class="card-preview-box"> <div class="card-preview-top"> <div class="card-sim-chip"></div> <div class="card-brand-logo"><i class="fa-brands fa-cc-visa"></i> VISA</div> </div> <div class="card-number-display" id="card-preview-num">•••• •••• •••• ••••</div> <div class="card-preview-bottom"> <div> <div>CARD HOLDER</div> <div class="card-holder-val" id="card-preview-name">YOUR NAME</div> </div> <div> <div>EXPIRES</div> <div class="card-expiry-val" id="card-preview-exp">MM/YY</div> </div> </div> </div> <!-- Card Form Inputs --> <div class="card-form-grid"> <div class="card-form-field full"> <label>หมายเลขบัตร (16 หลัก)</label> <input type="text" id="card-inp-num" placeholder="4000 1234 5678 9010" maxlength="19" oninput="formatCardNumber(this)"> </div> <div class="card-form-field full"> <label>ชื่อบนหน้าบัตร</label> <input type="text" id="card-inp-name" placeholder="เช่น SOMCHAI JAIDEE" oninput="updateCardName(this)"> </div> <div class="card-form-field"> <label>วันหมดอายุ (MM/YY)</label> <input type="text" id="card-inp-exp" placeholder="12/28" maxlength="5" oninput="formatCardExpiry(this)"> </div> <div class="card-form-field"> <label>รหัสความปลอดภัย (CVV)</label> <input type="password" id="card-inp-cvv" placeholder="•••" maxlength="3"> </div> </div> </div> <!-- 3. Bank Transfer Content --> <div class="pay-content-box" id="pay-content-bank" style="display:none;"> <div style="font-size:12px; color:var(--text-700); font-weight:600;">เลือกบัญชีธนาคารสำหรับโอนเงิน: </div> <div class="bank-select-grid"> <div class="bank-option-card active" onclick="selectBank(this, '089-2-33412-1', 'กสิกรไทย (KBANK)')"> <div class="bank-badge bank-kbank">KBANK</div> <div> <div class="bank-name">กสิกรไทย</div> <div class="bank-acc">089-2-33412-1</div> </div> </div> <div class="bank-option-card" onclick="selectBank(this, '405-1-88921-9', 'ไทยพาณิชย์ (SCB)')"> <div class="bank-badge bank-scb">SCB</div> <div> <div class="bank-name">ไทยพาณิชย์</div> <div class="bank-acc">405-1-88921-9</div> </div> </div> <div class="bank-option-card" onclick="selectBank(this, '101-9-45678-0', 'กรุงเทพ (BBL)')"> <div class="bank-badge bank-bbl">BBL</div> <div> <div class="bank-name">กรุงเทพ</div> <div class="bank-acc">101-9-45678-0</div> </div> </div> <div class="bank-option-card" onclick="selectBank(this, '012-4-99881-3', 'กรุงไทย (KTB)')"> <div class="bank-badge bank-ktb">KTB</div> <div> <div class="bank-name">กรุงไทย</div> <div class="bank-acc">012-4-99881-3</div> </div> </div> </div> <div style="background:var(--bg-card); border:1px solid var(--border); border-radius:var(--radius-sm); padding:10px 14px; font-size:12px; display:flex; justify-content:space-between; align-items:center;"> <div> <div style="color:var(--text-500); font-size:11px;">ชื่อบัญชี: บจก. โฮรุ แฟชั่น (ไทยแลนด์) </div> <div style="font-weight:700; color:var(--text-900);" id="selected-bank-display">กสิกรไทย: 089-2-33412-1</div> </div> <button class="copy-track-btn" onclick="copyTrackingNo('0892334121')"><i class="fa-regular fa-copy"></i> คัดลอก</button> </div> </div> <!-- 4. COD Content --> <div class="pay-content-box" id="pay-content-cod" style="display:none; text-align:center; padding:24px 20px;"> <i class="fa-solid fa-box-open" style="font-size:38px; color:var(--accent); margin-bottom:8px;"></i> <h4 style="font-size:14px; font-weight:700; color:var(--text-900);">บริการเก็บเงินปลายทาง (Cash on Delivery)</h4> <p style="font-size:12px; color:var(--text-500); max-width:380px; margin:6px auto 0; line-height:1.5;"> ท่านสามารถตรวจสอบพัสดุและชำระเงินสดแก่เจ้าหน้าที่ขนส่งเมื่อสินค้ามาถึงหน้าบ้าน </p> </div> <!-- Section 4: Coupons --> <div> <div class="payment-section-title"> <span><i class="fa-solid fa-ticket"></i> คูปองส่วนลด</span> </div> <div class="coupon-wrapper"> <input type="text" id="coupon-input" class="coupon-input" placeholder="กรอกโค้ดส่วนลด เช่น HORU50"> <button class="coupon-apply-btn" onclick="applyCoupon()"><i class="fa-solid fa-tag"></i> ใช้โค้ด</button> </div> <div class="coupon-tags-row"> <span class="coupon-tag" onclick="usePresetCoupon('HORU50')">⚡ HORU50 (ลด ฿50)</span> <span class="coupon-tag" onclick="usePresetCoupon('FREESHIP')">🚚 FREESHIP (ส่งด่วนฟรี)</span> <span class="coupon-tag" onclick="usePresetCoupon('DISCOUNT10')">🎉 DISCOUNT10 (ลด 10%)</span> </div> </div> <!-- Section 5: Order Summary Breakdown --> <div class="order-summary-card"> <div class="summary-line"> <span>ยอดรวมสินค้า (<span id="pay-items-count">0</span> ชิ้น)</span> <span id="pay-subtotal">฿0</span> </div> <div class="summary-line"> <span>ค่าจัดส่ง</span> <span id="pay-shipping-fee" style="color:#10b981;">ฟรี</span> </div> <div class="summary-line" id="pay-discount-row" style="display:none; color:#ef4444;"> <span>ส่วนลดจากคูปอง (<span id="applied-coupon-name"></span>)</span> <span id="pay-discount-amount">-฿0</span> </div> <div class="summary-line total"> <span>ยอดชำระสุทธิ</span> <span class="total-amount" id="pay-grand-total">฿0</span> </div> </div> <!-- Submit Button --> <button class="pay-confirm-btn" id="pay-submit-btn" onclick="processPayment()"> <i class="fa-solid fa-lock"></i> <span id="pay-btn-label">ยืนยันการชำระเงิน</span> </button> </div> </div> </div> <!-- Toast --> <div class="toast" id="toast"> <i class="fa-solid fa-check-circle"></i> <span id="toast-msg">เพิ่มสินค้าลงตะกร้าแล้ว</span> </div> <!-- ===== JAVASCRIPT LOGIC ===== --> <script> /* ===== Theme Settings ===== */ function toggleTheme() { const html = document.documentElement; const next = html.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'; html.setAttribute('data-theme', next); localStorage.setItem('horu-theme', next); } (function () { const saved = localStorage.getItem('horu-theme'); if (saved) document.documentElement.setAttribute('data-theme', saved); })(); /* ===== User Authentication System with Per-Email Data & Usage Logger ===== */ let currentUser = null; const DEFAULT_DEMO_ORDERS = [ { id: 'ORU-2026-00912', trackingNo: 'TH-HORU9920141', courier: 'HORU Express', status: 'shipping', statusLabel: 'กำลังนำจ่ายพัสดุ', step: 4, date: '25 ส.ค. 2026', eta: 'วันนี้ (ภายใน 18:00 น.)', driver: { name: 'คุณสมชาย ใจดี', phone: '089-123-4567', vehicle: 'ฮอนด้า เวฟ 125 (1กข-8921 กทม.)' }, timeline: [ { time: '14:20 น. (วันนี้)', desc: 'พนักงานขนส่งกำลังนำจ่ายพัสดุให้คุณ', location: 'ศูนย์กระจายพัสดุปลายทาง', active: true }, { time: '09:15 น. (วันนี้)', desc: 'พัสดุถึงศูนย์คัดแยกสินค้าหลัก กรุงเทพฯ', location: 'BKK Hub บางนา', active: false }, { time: '20:30 น. (เมื่อวาน)', desc: 'บริษัทขนส่งเข้ารับพัสดุจากร้านค้าเรียบร้อยแล้ว', location: 'จุดรับพัสดุคลังสินค้า', active: false }, { time: '16:00 น. (เมื่อวาน)', desc: 'ร้านค้าจัดเตรียมสินค้าและบรรจุลงกล่องสำเร็จ', location: 'HORU Store', active: false }, { time: '15:30 น. (เมื่อวาน)', desc: 'ชำระเงินและยืนยันคำสั่งซื้อสำเร็จ', location: 'ระบบออนไลน์', active: false } ], items: [ { name: 'แจ็คเก็ตลุคเกาหลี Oversized Varsity', size: 'L', qty: 1, price: 890, image: 'https://images.unsplash.com/photo-1591047139829-d91aecb6caea?w=100' } ], total: 890 }, { id: 'ORU-2026-00821', trackingNo: 'TH-FLASH7481920', courier: 'Flash Express', status: 'delivered', statusLabel: 'จัดส่งสำเร็จแล้ว', step: 5, date: '28 ก.ค. 2026', eta: 'จัดส่งสำเร็จเมื่อ 28 ก.ค. 2026', driver: { name: 'คุณวิชัย ขยันส่ง', phone: '081-987-6543', vehicle: 'โตโยต้า ไฮลักซ์ รีโว่ (3ฒข-4512)' }, timeline: [ { time: '15:42 น. (28 ก.ค.)', desc: 'ผู้รับได้รับพัสดุเรียบร้อยแล้ว (ลงชื่อ: ผู้รับโดยตรง)', location: 'ที่อยู่ผู้รับ', active: true }, { time: '11:00 น. (28 ก.ค.)', desc: 'พนักงานนำจ่ายพัสดุออกจากสาขา', location: 'สาขาปลายทาง', active: false }, { time: '06:30 น. (28 ก.ค.)', desc: 'พัสดุถึงสาขาปลายทางเตรียมนำจ่าย', location: 'ศูนย์กระจายสินค้า', active: false }, { time: '21:00 น. (27 ก.ค.)', desc: 'พัสดุอยู่ระหว่างการขนส่ง', location: 'ศูนย์คัดแยกหลัก', active: false }, { time: '14:15 น. (27 ก.ค.)', desc: 'ร้านค้าส่งมอบพัสดุให้ขนส่ง', location: 'HORU Store', active: false } ], items: [ { name: 'เสื้อผ้าสกรีนสีดำ สไตล์มินิมอลเกาหลี', size: 'M', qty: 1, price: 125, image: 'https://images.unsplash.com/photo-1503342217505-b0a15ec3261c?w=100' }, { name: 'เสื้อโปโลผ้าฝ้ายแท้ ทรงเข้ารูปดูดี', size: 'L', qty: 2, price: 127, image: 'https://images.unsplash.com/photo-1581655353564-df123a1eb820?w=100' } ], total: 379 }, { id: 'ORU-2026-00754', trackingNo: 'TH-KERRY3910284', courier: 'Kerry Express', status: 'delivered', statusLabel: 'จัดส่งสำเร็จแล้ว', step: 5, date: '14 ก.ค. 2026', eta: 'จัดส่งสำเร็จเมื่อ 14 ก.ค. 2026', driver: { name: 'คุณประเสริฐ รวดเร็ว', phone: '086-456-7890', vehicle: 'อีซูซุ ดีแมกซ์ (2กบ-1122)' }, timeline: [ { time: '13:10 น. (14 ก.ค.)', desc: 'เซ็นรับพัสดุเรียบร้อยแล้ว', location: 'ที่อยู่ผู้รับ', active: true }, { time: '09:00 น. (14 ก.ค.)', desc: 'พนักงานกำลังนำส่งพัสดุ', location: 'สาขาปลายทาง', active: false }, { time: '15:00 น. (13 ก.ค.)', desc: 'รับพัสดุเข้าระบบ Kerry Express', location: 'Kerry Shop', active: false } ], items: [ { name: 'กางเกงขายาว Slim Fit สีกรมท่า', size: '32', qty: 1, price: 390, image: 'https://images.unsplash.com/photo-1542272604-787c3835535d?w=100' } ], total: 390 } ]; let sampleOrders = []; let currentOrderTab = 'all'; let currentTrackingOrderId = ''; function checkUserSession() { const savedUser = localStorage.getItem('horu-user'); if (savedUser) { currentUser = JSON.parse(savedUser); loadUserData(currentUser.email); } else { currentUser = null; sampleOrders = []; cart = []; } updateUserNavbar(); updateCartUI(); } function loadUserData(email) { if (!email) return; const normEmail = email.toLowerCase().trim(); // 1. Load User Orders const savedOrders = localStorage.getItem('horu_orders_' + normEmail); if (savedOrders) { sampleOrders = JSON.parse(savedOrders); } else { if (normEmail === 'user@horu.com') { sampleOrders = JSON.parse(JSON.stringify(DEFAULT_DEMO_ORDERS)); localStorage.setItem('horu_orders_' + normEmail, JSON.stringify(sampleOrders)); } else { sampleOrders = []; } } // 2. Load User Cart const savedCart = localStorage.getItem('horu_cart_' + normEmail); cart = savedCart ? JSON.parse(savedCart) : []; // 3. Record User Usage Log recordUserActivity('เข้าสู่ระบบ', 'เข้าสู่ระบบสำเร็จ'); } function saveUserOrders() { if (!currentUser || !currentUser.email) return; const normEmail = currentUser.email.toLowerCase().trim(); localStorage.setItem('horu_orders_' + normEmail, JSON.stringify(sampleOrders)); } function saveUserCart() { if (!currentUser || !currentUser.email) return; const normEmail = currentUser.email.toLowerCase().trim(); localStorage.setItem('horu_cart_' + normEmail, JSON.stringify(cart)); } function recordUserActivity(actionTitle, actionDetail) { if (!currentUser || !currentUser.email) return; const normEmail = currentUser.email.toLowerCase().trim(); const usageKey = 'horu_usage_' + normEmail; let usage = JSON.parse(localStorage.getItem(usageKey) || '{"loginCount":0,"logs":[]}'); const nowStr = new Date().toLocaleString('th-TH', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); if (actionTitle === 'เข้าสู่ระบบ') { usage.loginCount = (usage.loginCount || 0) + 1; usage.lastLogin = nowStr; } if (!usage.logs) usage.logs = []; usage.logs.unshift({ action: actionTitle, detail: actionDetail || '', time: nowStr }); if (usage.logs.length > 20) usage.logs = usage.logs.slice(0, 20); localStorage.setItem(usageKey, JSON.stringify(usage)); } function updateUserNavbar() { const container = document.getElementById('user-navbar-section'); if (!container) return; if (currentUser) { const normEmail = currentUser.email.toLowerCase().trim(); const extras = JSON.parse(localStorage.getItem('horu_profile_' + normEmail) || '{}'); const displayName = extras.displayName || currentUser.email.split('@')[0]; container.innerHTML = ` <div class="user-profile-container"> <button class="user-avatar-btn" id="user-menu-btn" onclick="toggleUserDropdown(event)"> <img src="https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100" alt="Avatar"> <span>${displayName}</span> <i class="fa-solid fa-chevron-down" style="font-size: 10px; margin-left: 2px;"></i> </button> <div class="user-dropdown" id="user-dropdown-menu"> <div class="dropdown-item" style="font-weight: 700; border-bottom: 1px solid var(--border); color: var(--accent);"><i class="fa-solid fa-circle-user"></i> บัญชี: ${currentUser.email}</div> <div class="dropdown-item" onclick="openProfileModal()"><i class="fa-solid fa-id-card"></i> โปรไฟล์ & ประวัติการใช้งาน</div> <div class="dropdown-item" onclick="openOrdersModal()"><i class="fa-solid fa-clock-rotate-left"></i> รายการสั่งซื้อ (${sampleOrders.length})</div> <div class="dropdown-item" onclick="openTrackingModal()"><i class="fa-solid fa-truck-fast"></i> ติดตามสินค้า</div> <a href="admin.html" target="_blank" class="dropdown-item" style="color: var(--accent); text-decoration: none;"><i class="fa-solid fa-shield-halved"></i> แผงควบคุม Admin</a> <div class="dropdown-item logout" onclick="handleLogout()"><i class="fa-solid fa-arrow-right-from-bracket"></i> ออกจากระบบ</div> </div> </div> `; } else { container.innerHTML = ` <button class="login-btn" onclick="openLoginModal()"> <i class="fa-solid fa-arrow-right-to-bracket"></i> <span>เข้าสู่ระบบ</span> </button> `; } } function toggleUserDropdown(event) { event.stopPropagation(); const menu = document.getElementById('user-dropdown-menu'); if (menu) menu.classList.toggle('show'); } window.addEventListener('click', (e) => { const dropdown = document.getElementById('user-dropdown-menu'); const btn = document.getElementById('user-menu-btn'); if (dropdown && dropdown.classList.contains('show') && !btn?.contains(e.target) && !dropdown.contains(e.target)) { dropdown.classList.remove('show'); } }); function openLoginModal() { document.getElementById('login-modal').classList.add('show'); } function closeLoginModal(e) { if (!e || e.target === document.getElementById('login-modal') || e.target.closest('.modal-close-btn') || e.target.classList.contains('btn-cancel')) { document.getElementById('login-modal').classList.remove('show'); document.getElementById('login-form').reset(); document.getElementById('signup-form').reset(); toggleAuthMode('login'); } } function toggleAuthMode(mode) { const loginForm = document.getElementById('login-form'); const signupForm = document.getElementById('signup-form'); const modalTitle = document.getElementById('auth-modal-title'); if (mode === 'signup') { loginForm.style.display = 'none'; signupForm.style.display = 'block'; modalTitle.innerHTML = '<i class="fa-solid fa-user-plus" style="color: var(--accent); margin-right: 6px;"></i>สมัครสมาชิก HORU'; } else { loginForm.style.display = 'block'; signupForm.style.display = 'none'; modalTitle.innerHTML = '<i class="fa-solid fa-lock" style="color: var(--accent); margin-right: 6px;"></i>เข้าสู่ระบบ HORU'; } } function handleLoginSubmit(event) { event.preventDefault(); const email = document.getElementById('login-email').value.trim(); const password = document.getElementById('login-password').value; let users = JSON.parse(localStorage.getItem('horu-registered-users')) || []; const registeredUser = users.find(u => u.email.toLowerCase() === email.toLowerCase()); if (registeredUser) { if (registeredUser.password !== password) { alert('⚠️ รหัสผ่านไม่ถูกต้อง'); return; } } else if (email.toLowerCase() === 'user@horu.com') { if (password !== 'password123') { alert('⚠️ รหัสผ่านไม่ถูกต้องสำหรับบัญชีทดสอบ'); return; } } else { // Auto create user for convenience if password provided users.push({ name: email.split('@')[0], email: email, password: password }); localStorage.setItem('horu-registered-users', JSON.stringify(users)); } currentUser = { email: email }; localStorage.setItem('horu-user', JSON.stringify(currentUser)); loadUserData(email); updateUserNavbar(); filterProducts(); document.getElementById('login-modal').classList.remove('show'); document.getElementById('login-form').reset(); showToast(`👋 ยินดีต้อนรับคุณ ${email.split('@')[0]} เข้าสู่ HORU`); } function handleSignupSubmit(event) { event.preventDefault(); const name = document.getElementById('signup-name').value.trim(); const email = document.getElementById('signup-email').value.trim(); const password = document.getElementById('signup-password').value; const confirm = document.getElementById('signup-confirm').value; if (password !== confirm) { alert('⚠️ รหัสผ่านไม่ตรงกัน กรุณาตรวจสอบอีกครั้ง'); return; } let users = JSON.parse(localStorage.getItem('horu-registered-users')) || []; if (users.some(u => u.email.toLowerCase() === email.toLowerCase())) { alert('⚠️ อีเมลนี้มีอยู่ในระบบแล้ว กรุณาเข้าสู่ระบบ'); return; } users.push({ name, email, password }); localStorage.setItem('horu-registered-users', JSON.stringify(users)); // Save per-user profile const extras = { displayName: name, phone: '', address: '' }; localStorage.setItem('horu_profile_' + email.toLowerCase().trim(), JSON.stringify(extras)); currentUser = { email: email }; localStorage.setItem('horu-user', JSON.stringify(currentUser)); loadUserData(email); updateUserNavbar(); filterProducts(); document.getElementById('login-modal').classList.remove('show'); document.getElementById('signup-form').reset(); showToast(`🎉 ยินดีต้อนรับสมาชิกใหม่! คุณ ${name}`); } function handleLogout() { if (currentUser) { recordUserActivity('ออกจากระบบ', 'ออกจากระบบเรียบร้อย'); } currentUser = null; localStorage.removeItem('horu-user'); sampleOrders = []; cart = []; updateCartUI(); const cartPanel = document.getElementById('cart-panel'); const cartOverlay = document.getElementById('cart-overlay'); if (cartPanel && cartPanel.classList.contains('open')) cartPanel.classList.remove('open'); if (cartOverlay && cartOverlay.classList.contains('show')) cartOverlay.classList.remove('show'); updateUserNavbar(); filterProducts(); showToast('🔒 ออกจากระบบเรียบร้อยแล้ว'); } /* ===== Profile Modal Functions ===== */ function openProfileModal() { if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนดูข้อมูลโปรไฟล์ครับ'); openLoginModal(); return; } const dropdown = document.getElementById('user-dropdown-menu'); if (dropdown) dropdown.classList.remove('show'); const normEmail = currentUser.email.toLowerCase().trim(); const extras = JSON.parse(localStorage.getItem('horu_profile_' + normEmail) || '{}'); const username = currentUser.email.split('@')[0]; document.getElementById('profile-display-name').textContent = extras.displayName || username; document.getElementById('profile-display-email').textContent = currentUser.email; document.getElementById('profile-input-name').value = extras.displayName || username; document.getElementById('profile-input-phone').value = extras.phone || ''; document.getElementById('profile-input-address').value = extras.address || ''; // Count orders for this specific email document.getElementById('profile-stat-orders').textContent = sampleOrders.length; // Load and display usage logs for this email const usageKey = 'horu_usage_' + normEmail; const usage = JSON.parse(localStorage.getItem(usageKey) || '{"loginCount":1,"lastLogin":"—","logs":[]}'); const lastLoginEl = document.getElementById('profile-last-login'); const loginCountEl = document.getElementById('profile-login-count'); const actListEl = document.getElementById('profile-activity-list'); if (lastLoginEl) lastLoginEl.textContent = usage.lastLogin || 'เมื่อสักครู่'; if (loginCountEl) loginCountEl.textContent = usage.loginCount || 1; if (actListEl) { if (!usage.logs || usage.logs.length === 0) { actListEl.innerHTML = `<div style="color:var(--text-500); text-align:center; padding:10px;">ยังไม่มีบันทึกประวัติการใช้งาน</div>`; } else { actListEl.innerHTML = usage.logs.map(lg => ` <div style="background:var(--bg-card); border:1px solid var(--border); padding:6px 10px; border-radius:6px; display:flex; justify-content:space-between; align-items:center;"> <div> <span style="font-weight:700; color:var(--accent);">${lg.action}</span> ${lg.detail ? `<span style="color:var(--text-700); margin-left:6px;">(${lg.detail})</span>` : ''} </div> <span style="color:var(--text-500); font-size:10.5px;">${lg.time}</span> </div> `).join(''); } } document.getElementById('profile-modal').classList.add('show'); } function closeProfileModal(e) { if (!e || e.target === document.getElementById('profile-modal') || !e.target.closest) { document.getElementById('profile-modal').classList.remove('show'); } else if (e.target === document.getElementById('profile-modal')) { document.getElementById('profile-modal').classList.remove('show'); } } function saveProfileData() { if (!currentUser) return; const normEmail = currentUser.email.toLowerCase().trim(); const displayName = document.getElementById('profile-input-name').value.trim(); const phone = document.getElementById('profile-input-phone').value.trim(); const address = document.getElementById('profile-input-address').value.trim(); if (!displayName) { showToast('⚠️ กรุณาระบุชื่อที่แสดง'); return; } const extras = { displayName, phone, address }; localStorage.setItem('horu_profile_' + normEmail, JSON.stringify(extras)); document.getElementById('profile-display-name').textContent = displayName; recordUserActivity('บันทึกข้อมูลส่วนตัว', `ชื่อ: ${displayName}`); updateUserNavbar(); showToast('✅ บันทึกข้อมูลโปรไฟล์เรียบร้อยแล้ว'); } /* ===== Orders Modal Functions ===== */ function openOrdersModal() { if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนดูรายการสั่งซื้อครับ'); openLoginModal(); return; } const dropdown = document.getElementById('user-dropdown-menu'); if (dropdown) dropdown.classList.remove('show'); currentOrderTab = 'all'; renderOrdersList(); document.getElementById('orders-modal').classList.add('show'); } function closeOrdersModal(e) { if (!e || e.target === document.getElementById('orders-modal')) { document.getElementById('orders-modal').classList.remove('show'); } } function switchOrderTab(tab) { currentOrderTab = tab; document.querySelectorAll('.orders-tab-btn').forEach(b => b.classList.remove('active')); document.getElementById('tab-' + tab).classList.add('active'); renderOrdersList(); } function renderOrdersList() { const container = document.getElementById('orders-list-container'); const statusMap = { all: null, shipping: 'shipping', delivered: 'delivered' }; const filterStatus = statusMap[currentOrderTab]; const filtered = filterStatus ? sampleOrders.filter(o => o.status === filterStatus) : sampleOrders; if (filtered.length === 0) { container.innerHTML = ` <div class="orders-empty"> <i class="fa-solid fa-box-open"></i> <p>ไม่มีรายการสั่งซื้อสำหรับบัญชี (${currentUser ? currentUser.email : ''})</p> </div>`; return; } container.innerHTML = filtered.map(order => ` <div class="order-card"> <div class="order-card-head"> <div> <span class="order-id"># ${order.id}</span> <span style="font-size:11px; color:var(--text-500); margin-left:6px;">(${order.courier || 'HORU Express'})</span> </div> <span class="order-status-badge ${order.status}">${order.statusLabel}</span> </div> <div class="order-items-list"> ${order.items.map(item => ` <div class="order-item-row"> <img class="order-item-img" src="${item.image}" alt="${item.name}" onerror="this.src='https://via.placeholder.com/48'"> <div class="order-item-info"> <div class="order-item-name">${item.name}</div> <div class="order-item-sub">ไซส์ ${item.size} × ${item.qty} ชิ้น</div> </div> <span class="order-item-price">฿${(item.price * item.qty).toLocaleString()}</span> </div>`).join('')} </div> <div class="order-card-foot"> <span class="order-date"><i class="fa-regular fa-calendar"></i> ${order.date}</span> <div style="display:flex; align-items:center; gap:10px;"> <span class="order-total">รวม ฿${order.total.toLocaleString()}</span> <button class="order-track-btn" onclick="openTrackingModal('${order.id}')"> <i class="fa-solid fa-truck-fast"></i> ติดตามพัสดุ </button> </div> </div> </div>`).join(''); } /* ===== TRACKING SYSTEM CONTROLLER ===== */ function openTrackingModal(orderIdOrTrackNo) { // Check if user is logged in if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนติดตามสถานะพัสดุครับ'); openLoginModal(); return; } const dropdown = document.getElementById('user-dropdown-menu'); if (dropdown) dropdown.classList.remove('show'); const ordersModal = document.getElementById('orders-modal'); if (ordersModal) ordersModal.classList.remove('show'); let targetOrder = null; if (orderIdOrTrackNo) { targetOrder = sampleOrders.find(o => o.id.toLowerCase() === orderIdOrTrackNo.toLowerCase() || (o.trackingNo && o.trackingNo.toLowerCase() === orderIdOrTrackNo.toLowerCase()) ); } if (!targetOrder && sampleOrders.length > 0) { // Default to latest or shipping order targetOrder = sampleOrders.find(o => o.status === 'shipping') || sampleOrders[0]; } if (targetOrder) { currentTrackingOrderId = targetOrder.id; const searchInp = document.getElementById('track-search-input'); if (searchInp) searchInp.value = targetOrder.id; renderTrackingDetail(targetOrder); } else { currentTrackingOrderId = ''; renderTrackingEmpty(); } renderQuickChips(currentTrackingOrderId); document.getElementById('tracking-modal').classList.add('show'); } function closeTrackingModal(e) { if (!e || e.target === document.getElementById('tracking-modal')) { document.getElementById('tracking-modal').classList.remove('show'); } } function renderQuickChips(activeId) { const chipsEl = document.getElementById('track-quick-chips'); if (!chipsEl) return; if (sampleOrders.length === 0) { chipsEl.innerHTML = ''; return; } chipsEl.innerHTML = ` <span class="track-quick-label"><i class="fa-solid fa-receipt"></i> คำสั่งซื้อของคุณ:</span> ${sampleOrders.map(o => ` <button class="track-chip ${o.id === activeId ? 'active' : ''}" onclick="openTrackingModal('${o.id}')"> <span>#${o.id}</span> <span style="opacity:0.75; font-size:10px;">(${o.status === 'delivered' ? 'ส่งแล้ว' : 'กำลังส่ง'})</span> </button> `).join('')} `; } function searchTracking() { if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนค้นหาพัสดุครับ'); openLoginModal(); return; } const query = document.getElementById('track-search-input').value.trim().toLowerCase(); if (!query) { showToast('⚠️ กรุณากรอกรหัสคำสั่งซื้อ หรือ เลขพัสดุ'); return; } const found = sampleOrders.find(o => o.id.toLowerCase() === query || (o.trackingNo && o.trackingNo.toLowerCase() === query) ); if (found) { currentTrackingOrderId = found.id; renderTrackingDetail(found); renderQuickChips(found.id); showToast(`📦 พบข้อมูลพัสดุ #${found.id}`); } else { const container = document.getElementById('tracking-detail-container'); container.innerHTML = ` <div class="orders-empty" style="padding: 36px 16px;"> <i class="fa-solid fa-magnifying-glass-location" style="font-size:44px; color:var(--accent); opacity:0.6;"></i> <h4 style="font-size:15px; font-weight:700; color:var(--text-900); margin-top:8px;">ไม่พบข้อมูลพัสดุ "${query}"</h4> <p style="font-size:12.5px; color:var(--text-500); margin-top:4px;">โปรดตรวจสอบรหัสคำสั่งซื้อหรือเลขพัสดุใหม่อีกครั้ง หรือเลือกจากรายการด้านบน</p> </div> `; } } function renderTrackingDetail(order) { const container = document.getElementById('tracking-detail-container'); if (!container) return; const step = order.step || (order.status === 'delivered' ? 5 : 4); const progressPercent = Math.min(100, Math.max(0, (step - 1) * 25)); const steps = [ { num: 1, title: 'สั่งซื้อสำเร็จ', icon: 'fa-solid fa-file-invoice' }, { num: 2, title: 'เตรียมพัสดุ', icon: 'fa-solid fa-box-archive' }, { num: 3, title: 'ขนส่งรับพัสดุ', icon: 'fa-solid fa-truck' }, { num: 4, title: 'กำลังนำจ่าย', icon: 'fa-solid fa-motorcycle' }, { num: 5, title: 'จัดส่งสำเร็จ', icon: 'fa-solid fa-house-chimney-check' } ]; const isDelivered = step >= 5 || order.status === 'delivered'; container.innerHTML = ` <div class="track-card"> <!-- Top courier & tracking number --> <div class="track-card-top"> <div class="track-courier-info"> <div class="track-courier-logo"> <i class="fa-solid fa-truck-fast"></i> </div> <div> <div class="track-courier-name">${order.courier || 'HORU Express'}</div> <div class="track-num-wrap"> <span class="track-num">เลขพัสดุ: ${order.trackingNo || 'TH-HORU8839120'}</span> <button class="copy-track-btn" onclick="copyTrackingNo('${order.trackingNo || 'TH-HORU8839120'}')" title="คัดลอกเลขพัสดุ"> <i class="fa-regular fa-copy"></i> </button> </div> </div> </div> <div class="track-eta-wrap"> <div class="track-eta-label">สถานะปัจจุบัน</div> <div class="track-eta-val"> <span class="order-status-badge ${order.status}">${order.statusLabel}</span> </div> <div style="font-size:11px; color:var(--text-500); margin-top:4px;"> <i class="fa-regular fa-clock"></i> ${order.eta || 'ประมาณการส่ง: วันนี้'} </div> </div> </div> <!-- Horizontal Stepper --> <div class="track-stepper"> <div class="track-stepper-line"></div> <div class="track-stepper-progress" style="width: calc(${progressPercent}% - 60px);"></div> ${steps.map(s => { let stateClass = ''; if (s.num < step) stateClass = 'completed'; else if (s.num === step) stateClass = 'current'; return ` <div class="track-step-item ${stateClass}"> <div class="track-step-icon"> <i class="${s.num < step ? 'fa-solid fa-check' : s.icon}"></i> </div> <span class="track-step-title">${s.title}</span> </div> `; }).join('')} </div> <!-- Driver / Courier contact box (if available) --> ${order.driver ? ` <div class="track-driver-box"> <div class="track-driver-left"> <div class="track-driver-avatar"> <i class="fa-solid fa-user-ninja"></i> </div> <div> <div class="track-driver-name">${order.driver.name} (พนักงานจัดส่ง)</div> <div class="track-driver-meta"><i class="fa-solid fa-phone"></i> ${order.driver.phone} • ${order.driver.vehicle}</div> </div> </div> <div class="track-actions-bar"> <button class="track-action-btn" onclick="speedUpDelivery('${order.id}')"> <i class="fa-solid fa-bolt" style="color:#f59e0b;"></i> เร่งการจัดส่ง </button> ${!isDelivered ? ` <button class="track-action-btn primary" onclick="confirmOrderDelivered('${order.id}')"> <i class="fa-solid fa-circle-check"></i> ยืนยันรับสินค้าแล้ว </button> ` : ''} </div> </div> ` : ''} <!-- Timeline history --> <div class="track-timeline-wrap"> <div class="track-timeline-title"> <i class="fa-solid fa-route" style="color:var(--accent);"></i> บันทึกเส้นทางการจัดส่งพัสดุ </div> <div class="track-timeline-list"> ${(order.timeline || []).map((tl, idx) => ` <div class="track-timeline-node ${idx === 0 ? 'active' : ''}"> <div class="track-timeline-dot"></div> <div class="track-timeline-desc">${tl.desc}</div> <div class="track-timeline-time"> <span>${tl.time}</span> ${tl.location ? `<span class="track-timeline-location"><i class="fa-solid fa-location-dot" style="font-size:9px; margin-right:2px;"></i>${tl.location}</span>` : ''} </div> </div> `).join('')} </div> </div> <!-- Items in Parcel --> <div style="background:var(--bg-card); border:1px solid var(--border); border-radius:var(--radius-md); padding:14px 16px;"> <div style="font-size:12px; font-weight:700; color:var(--text-700); margin-bottom:10px; display:flex; justify-content:space-between;"> <span><i class="fa-solid fa-bag-shopping" style="color:var(--accent); margin-right:4px;"></i> สินค้าในพัสดุนี้ (${order.items.length} รายการ)</span> <span style="font-family:'Outfit', sans-serif; color:var(--text-900);">รวม ฿${order.total.toLocaleString()}</span> </div> <div class="order-items-list" style="margin-bottom:0;"> ${order.items.map(item => ` <div class="order-item-row"> <img class="order-item-img" src="${item.image}" alt="${item.name}" onerror="this.src='https://via.placeholder.com/48'"> <div class="order-item-info"> <div class="order-item-name">${item.name}</div> <div class="order-item-sub">ไซส์ ${item.size || 'Free'} × ${item.qty} ชิ้น</div> </div> <span class="order-item-price">฿${(item.price * item.qty).toLocaleString()}</span> </div> `).join('')} </div> </div> </div> `; } function renderTrackingEmpty() { const container = document.getElementById('tracking-detail-container'); if (container) { container.innerHTML = ` <div class="orders-empty"> <i class="fa-solid fa-truck-ramp-box"></i> <p>ยังไม่มีรายการพัสดุที่กำลังติดตาม</p> </div> `; } } function copyTrackingNo(trackingNum) { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(trackingNum); } showToast(`📋 คัดลอกหมายเลขพัสดุ ${trackingNum} แล้ว`); } function speedUpDelivery(orderId) { showToast(`⚡ ส่งการแจ้งเตือน "เร่งนำจ่ายด่วน" ไปยังพนักงานขนส่งแล้ว!`); } function confirmOrderDelivered(orderId) { const order = sampleOrders.find(o => o.id === orderId); if (!order) return; order.status = 'delivered'; order.statusLabel = 'จัดส่งสำเร็จแล้ว'; order.step = 5; order.eta = 'จัดส่งสำเร็จเรียบร้อยแล้ว'; if (!order.timeline) order.timeline = []; order.timeline.unshift({ time: 'เมื่อสักครู่', desc: 'ผู้รับยืนยันการรับพัสดุสำเร็จผ่านระบบ HORU', location: 'ที่อยู่ผู้รับ', active: true }); renderTrackingDetail(order); renderQuickChips(order.id); if (document.getElementById('orders-modal').classList.contains('show')) { renderOrdersList(); } showToast(`🎉 ยืนยันการรับสินค้า #${order.id} เรียบร้อยแล้ว ขอขอบคุณที่ใช้บริการ HORU!`); } /* ===== Initial Dynamic Products Array (20 Items) ===== */ let products = [ { id: 1, brand: 'MOSESQUEEN', name: 'เสื้อผ้าสกรีนสีดำ แขนสั้น สไตล์มินิมอลเกาหลี', price: 125, originalPrice: 299, badge: 'Official', rating: 5.0, sold: '2.1k', image: 'https://images.unsplash.com/photo-1503342217505-b0a15ec3261c?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L', 'XL'], salesCount: 2100, dateAdded: 5, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 2, brand: 'DIV POLO', name: 'เสื้อโปโลผู้ชายผ้าฝ้ายแท้ ทรงเข้ารูปดูดี', price: 127, originalPrice: 159, badge: 'Hot', rating: 4.8, sold: '980', image: 'https://images.unsplash.com/photo-1581655353564-df123a1eb820?w=500', location: 'ในประเทศ', sizes: ['M', 'L', 'XL'], salesCount: 980, dateAdded: 12, category: 'เสื้อโปโล เสื้อผ้าผู้ชาย' }, { id: 3, brand: 'TEE-SHOP', name: 'เสื้อยืด Oversize สกรีนลายสตรีทยอดฮิต', price: 88, originalPrice: 163, badge: 'Hot', rating: 4.9, sold: '5.4k', image: 'https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L', 'XL'], salesCount: 5400, dateAdded: 3, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 4, brand: 'JACQUARD STUDIO', name: 'เสื้อยืดแขนยาว ปักลาย สไตล์เรียบหรู ใส่ทำงานได้', price: 126, originalPrice: 300, badge: 'Official', rating: 4.9, sold: '1.3k', image: 'https://images.unsplash.com/photo-1618354691373-d851c5c3a990?w=500', location: 'ต่างประเทศ', sizes: ['M', 'L'], salesCount: 1300, dateAdded: 15, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 5, brand: 'UNIQLO', name: 'เสื้อฮู้ดดี้ผ้าเฟลซ กันหนาว ซิปหน้า มีหลายสี', price: 590, originalPrice: 790, badge: 'Official', rating: 4.9, sold: '3.2k', image: 'https://images.unsplash.com/photo-1556821840-3a63f15732ce?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L', 'XL'], salesCount: 3200, dateAdded: 8, category: 'แจ็คเก็ต เสื้อผ้าผู้ชาย' }, { id: 6, brand: 'CASUAL WEAR', name: 'เสื้อเชิ้ตลายตารางหมากรุก ผ้าฝ้าย 100% แขนยาวคาวบอย', price: 49, originalPrice: 129, badge: 'Hot', rating: 4.6, sold: '8.7k', image: 'https://images.unsplash.com/photo-1598033129183-c4f50c736f10?w=500', location: 'ต่างประเทศ', sizes: ['S', 'M', 'L'], salesCount: 8700, dateAdded: 1, category: 'เสื้อเชิ้ต เสื้อผ้าผู้ชาย' }, { id: 7, brand: 'VINTAGE WASH', name: 'เสื้อยืดฟอกสี Washed สไตล์อเมริกัน วินเทจ', price: 249, originalPrice: 390, badge: 'Hot', rating: 4.7, sold: '620', image: 'https://images.unsplash.com/photo-1521572267360-ee0c2909d518?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L', 'XL'], salesCount: 620, dateAdded: 9, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 8, brand: 'COLORBOX', name: 'เสื้อยืดพิมพ์ลาย Graphic Art ลิขสิทธิ์แท้', price: 189, originalPrice: 350, badge: 'Official', rating: 4.8, sold: '1.8k', image: 'https://images.unsplash.com/photo-1503342217505-b0a15ec3261c?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L'], salesCount: 1800, dateAdded: 6, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 9, brand: 'GAP', name: 'แจ็คเก็ตกันลม Windbreaker น้ำหนักเบา ใส่ได้ทุกฤดู', price: 1290, originalPrice: 1990, badge: 'Official', rating: 4.9, sold: '450', image: 'https://images.unsplash.com/photo-1548036328-c9fa89d128fa?w=500', location: 'ต่างประเทศ', sizes: ['M', 'L', 'XL'], salesCount: 450, dateAdded: 20, category: 'แจ็คเก็ต เสื้อผ้าผู้ชาย' }, { id: 10, brand: 'BASIC WEAR', name: 'เสื้อยืดสีพื้น Cotton 100% นุ่มพิเศษ แพ็ค 3 ตัว', price: 199, originalPrice: 390, badge: 'Hot', rating: 4.7, sold: '12.3k', image: 'https://images.unsplash.com/photo-1521572267360-ee0c2909d518?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L', 'XL'], salesCount: 12300, dateAdded: 2, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 11, brand: 'ZARA', name: 'เสื้อเบลเซอร์ สูทผู้ชาย ผ้าวูล ทรง Slim Fit ดูดีมาก', price: 890, originalPrice: 1490, badge: 'Official', rating: 4.8, sold: '234', image: 'https://images.unsplash.com/photo-1507679799987-c73779587ccf?w=500', location: 'ต่างประเทศ', sizes: ['M', 'L', 'XL'], salesCount: 234, dateAdded: 25, category: 'สูท เสื้อผ้าผู้ชาย' }, { id: 12, brand: 'SPORT ACTIVE', name: 'เสื้อกล้ามออกกำลังกาย Dry-Fit ระบายอากาศดีเยี่ยม', price: 69, originalPrice: 149, badge: 'Hot', rating: 4.5, sold: '7.9k', image: 'https://images.unsplash.com/photo-1517838277536-f5f99be501cd?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L'], salesCount: 7900, dateAdded: 4, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 13, brand: 'FEAR OF GOD', name: 'ESSENTIALS เสื้อยืดคอกลมคลาสสิก ผ้าหนานุ่ม', price: 350, originalPrice: 590, badge: 'Official', rating: 4.9, sold: '3.5k', image: 'https://images.unsplash.com/photo-1521572267360-ee0c2909d518?w=500', location: 'ต่างประเทศ', sizes: ['S', 'M', 'L', 'XL'], salesCount: 3500, dateAdded: 7, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 14, brand: 'NIKE', name: 'เสื้อยืด Dri-FIT Training ออกกำลังกาย คล่องตัว', price: 490, originalPrice: 890, badge: 'Official', rating: 4.8, sold: '4.2k', image: 'https://images.unsplash.com/photo-1517838277536-f5f99be501cd?w=500', location: 'ต่างประเทศ', sizes: ['S', 'M', 'L', 'XL'], salesCount: 4200, dateAdded: 11, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 15, brand: 'ADIDAS', name: 'Track Jacket แจ็คเก็ตสไตล์สปอร์ต ยอดนิยมตลอดกาล', price: 1100, originalPrice: 1800, badge: 'Official', rating: 4.9, sold: '1.1k', image: 'https://images.unsplash.com/photo-1551028719-00167b16eac5?w=500', location: 'ต่างประเทศ', sizes: ['M', 'L', 'XL'], salesCount: 1100, dateAdded: 16, category: 'แจ็คเก็ต เสื้อผ้าผู้ชาย' }, { id: 16, brand: 'H&M', name: 'Linen Oversized Shirt เสื้อเชิ้ตผ้าลินิน บางเบาใส่สบาย', price: 399, originalPrice: 699, badge: 'Hot', rating: 4.7, sold: '2.1k', image: 'https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L'], salesCount: 2100, dateAdded: 10, category: 'เสื้อเชิ้ต เสื้อผ้าผู้ชาย' }, { id: 17, brand: 'LACOSTE', name: 'เสื้อโปโลลายปักสัญลักษณ์ แบรนด์แท้ ลุคสปอร์ตหรู', price: 950, originalPrice: 1500, badge: 'Official', rating: 4.9, sold: '850', image: 'https://images.unsplash.com/photo-1581655353564-df123a1eb820?w=500', location: 'ต่างประเทศ', sizes: ['M', 'L', 'XL'], salesCount: 850, dateAdded: 18, category: 'เสื้อโปโล เสื้อผ้าผู้ชาย' }, { id: 18, brand: 'STUSSY', name: 'เสื้อยืดสกรีนลายกราฟิกยอดฮิต สไตล์สตรีทแฟชั่น', price: 420, originalPrice: 750, badge: 'Hot', rating: 4.8, sold: '1.9k', image: 'https://images.unsplash.com/photo-1576566588028-4147f3842f27?w=500', location: 'ต่างประเทศ', sizes: ['S', 'M', 'L', 'XL'], salesCount: 1900, dateAdded: 13, category: 'เสื้อยืด เสื้อผ้าผู้ชาย' }, { id: 19, brand: 'LEVI\'S', name: 'เสื้อเชิ้ตยีนส์เดนิม คลาสสิก ย้อนยุคใส่ลุย ๆ', price: 750, originalPrice: 1200, badge: 'Official', rating: 4.9, sold: '670', image: 'https://images.unsplash.com/photo-1602810318383-e386cc2a3ccf?w=500', location: 'ในประเทศ', sizes: ['M', 'L', 'XL'], salesCount: 670, dateAdded: 14, category: 'เสื้อเชิ้ต เสื้อผ้าผู้ชาย' }, { id: 20, brand: 'GUESS', name: 'เสื้อเชิ้ตแฟชั่นพิมพ์ลายทาง ลายทางเท่ ๆ มีมิติ', price: 690, originalPrice: 1190, badge: 'Hot', rating: 4.6, sold: '540', image: 'https://images.unsplash.com/photo-1596755094514-f87e34085b2c?w=500', location: 'ในประเทศ', sizes: ['S', 'M', 'L'], salesCount: 540, dateAdded: 17, category: 'เสื้อเชิ้ต เสื้อผ้าผู้ชาย' } ]; /* ===== Filter & Sort State variables ===== */ let currentSort = 'popular'; let currentRatingFilter = null; let searchQuery = ''; /* ===== DOM Container ===== */ const gridContainer = document.getElementById('product-grid-container'); /* ===== Render Products Function ===== */ function renderProducts(productsList = products) { if (productsList.length === 0) { gridContainer.innerHTML = `<div style="grid-column: 1/-1; text-align: center; padding: 60px 0; color: var(--text-500); font-weight: 500;">ไม่พบสินค้าที่ต้องการ</div>`; return; } gridContainer.innerHTML = productsList.map(p => { const savePercent = p.originalPrice ? Math.round(((p.originalPrice - p.price) / p.originalPrice) * 100) : 0; // HTML structure for badge const badgeHTML = p.badge ? `<span class="card-badge badge-${p.badge.toLowerCase()}">${p.badge}</span>` : ''; // HTML structure for original price and save percentage const priceWasHTML = p.originalPrice ? `<span class="price-was">฿${p.originalPrice.toLocaleString()}</span>` : ''; const priceSaveHTML = savePercent > 0 ? `<span class="price-save">-${savePercent}%</span>` : ''; // Generate rating stars const starCount = Math.round(p.rating); const starsHTML = '★'.repeat(starCount) + '☆'.repeat(5 - starCount); // Generate sizes selector HTML const sizesHTML = p.sizes && p.sizes.length > 0 ? ` <div class="size-selector"> ${p.sizes.map((size, index) => ` <button class="size-btn ${index === 0 ? 'active' : ''}" onclick="event.stopPropagation(); selectSize(${p.id}, this)"> ${size} </button> `).join('')} </div> ` : ''; return ` <div class="product-card" id="p-card-${p.id}"> <div class="card-img"> <img src="${p.image}" alt="${p.name}"> ${badgeHTML} <button class="wishlist-btn" onclick="event.stopPropagation(); toggleWishlist(this)"> <i class="fa-regular fa-heart"></i> </button> </div> <div class="card-body"> <div class="card-brand">${p.brand}</div> <h3 class="card-title">${p.name}</h3> ${sizesHTML} <div class="card-price-row"> <span class="price-now">฿${p.price.toLocaleString()}</span> ${priceWasHTML} ${priceSaveHTML} </div> <div class="card-meta"> <span class="stars">${starsHTML}</span> <span class="rating">${p.rating.toFixed(1)}</span> <span class="dot"></span> <span class="sold">ขายแล้ว ${p.sold}</span> </div> </div> <div class="card-footer" onclick="event.stopPropagation()"> <div class="qty-row"> <span class="qty-label">จำนวน</span> <div class="qty-ctrl"> <button class="qty-btn" onclick="changeQty('qty-${p.id}', -1)">−</button> <input type="text" id="qty-${p.id}" value="1" class="qty-input" readonly> <button class="qty-btn" onclick="changeQty('qty-${p.id}', 1)">+</button> </div> </div> <button class="add-btn" onclick="addToCartFromCard(${p.id})"> <i class="fa-solid fa-bag-shopping"></i> หยิบใส่ตะกร้า </button> </div> </div> `; }).join(''); } /* ===== Select Size Logic on Card ===== */ function selectSize(productId, sizeBtn) { const card = document.getElementById(`p-card-${productId}`); if (card) { card.querySelectorAll('.size-btn').forEach(btn => btn.classList.remove('active')); sizeBtn.classList.add('active'); } } /* ===== Wishlist Toggle ===== */ function toggleWishlist(btn) { if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบเพื่อบันทึกสินค้าในรายการโปรดครับ'); openLoginModal(); return; } btn.classList.toggle('active'); const icon = btn.querySelector('i'); if (btn.classList.contains('active')) { icon.className = 'fa-solid fa-heart'; showToast('บันทึกในรายการโปรดแล้ว ❤️'); } else { icon.className = 'fa-regular fa-heart'; } } /* ===== Quantity Control ===== */ function changeQty(id, delta) { const el = document.getElementById(id); if (!el) return; let v = parseInt(el.value) + (delta || 0); if (v < 1) v = 1; el.value = v; } /* ===== Add Product Modal System ===== */ function openSellModal() { // Check if user is logged in if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนลงขายสินค้าครับ'); openLoginModal(); return; } document.getElementById('sell-modal').classList.add('show'); } function closeSellModal(e) { if (!e || e.target === document.getElementById('sell-modal') || e.target.closest('.modal-close-btn') || e.target.classList.contains('btn-cancel')) { document.getElementById('sell-modal').classList.remove('show'); document.getElementById('add-product-form').reset(); } } function saveProduct(event) { event.preventDefault(); if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนลงขายสินค้าครับ'); openLoginModal(); return; } const name = document.getElementById('p-name').value; const brand = document.getElementById('p-brand').value; const badge = document.getElementById('p-badge').value; const price = parseInt(document.getElementById('p-price').value); const originalPriceVal = document.getElementById('p-original').value; const originalPrice = originalPriceVal ? parseInt(originalPriceVal) : null; const image = document.getElementById('p-image').value; const rating = parseFloat(document.getElementById('p-rating').value) || 5.0; const location = document.getElementById('p-location').value; // Collect active sizes const sizes = []; if (document.getElementById('size-s').checked) sizes.push('S'); if (document.getElementById('size-m').checked) sizes.push('M'); if (document.getElementById('size-l').checked) sizes.push('L'); if (document.getElementById('size-xl').checked) sizes.push('XL'); const newProduct = { id: products.length + 1, brand: brand, name: name, price: price, originalPrice: originalPrice, badge: badge || null, rating: rating, sold: '0', image: image, location: location, sizes: sizes, salesCount: 0, dateAdded: 0, // Newest category: name + ' เสื้อผ้าผู้ชาย' }; // Add to array products.unshift(newProduct); // Re-render and close filterProducts(); document.getElementById('sell-modal').classList.remove('show'); document.getElementById('add-product-form').reset(); showToast('🎉 ลงขายสินค้าสำเร็จแล้ว!'); } /* ===== Cart Panel toggle ===== */ function toggleCart() { if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนใช้งานตะกร้าสินค้าครับ'); openLoginModal(); return; } document.getElementById('cart-panel').classList.toggle('open'); document.getElementById('cart-overlay').classList.toggle('show'); } /* ===== Add To Cart Logistics ===== */ let cart = []; function quickAdd(productId) { // Check if user is logged in if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนหยิบสินค้าใส่ตะกร้าครับ'); openLoginModal(); return; } const product = products.find(p => p.id === productId); if (!product) return; // Find default active size let size = ''; const card = document.getElementById(`p-card-${productId}`); if (card) { const activeSizeBtn = card.querySelector('.size-btn.active'); if (activeSizeBtn) size = activeSizeBtn.innerText.trim(); } else if (product.sizes && product.sizes.length > 0) { size = product.sizes[0]; } addToCart(product, 1, size); } function addToCartFromCard(productId) { // Check if user is logged in if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนหยิบสินค้าใส่ตะกร้าครับ'); openLoginModal(); return; } const product = products.find(p => p.id === productId); if (!product) return; const qtyInput = document.getElementById(`qty-${productId}`); const qty = qtyInput ? parseInt(qtyInput.value) : 1; let size = ''; const card = document.getElementById(`p-card-${productId}`); if (card) { const activeSizeBtn = card.querySelector('.size-btn.active'); if (activeSizeBtn) size = activeSizeBtn.innerText.trim(); } addToCart(product, qty, size); if (qtyInput) qtyInput.value = 1; } function addToCart(product, qty, size) { // Check if user is logged in if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนหยิบสินค้าใส่ตะกร้าครับ'); openLoginModal(); return; } const itemKey = `${product.name}-${size}`; const existing = cart.find(item => item.key === itemKey); if (existing) { existing.qty += qty; } else { cart.push({ key: itemKey, productId: product.id, name: product.name, price: product.price, qty: qty, size: size }); } updateCartUI(); saveUserCart(); if (!document.getElementById('cart-panel').classList.contains('open')) { document.getElementById('cart-panel').classList.add('open'); document.getElementById('cart-overlay').classList.add('show'); } showToast(`เพิ่ม "${product.name.split(' ').slice(0, 3).join(' ')}" ${size ? `ไซส์ ${size}` : ''} ลงตะกร้าแล้ว`); } function removeFromCart(itemKey) { const item = cart.find(i => i.key === itemKey); if (!item) return; cart = cart.filter(i => i.key !== itemKey); updateCartUI(); saveUserCart(); showToast(`นำ "${item.name.split(' ').slice(0, 3).join(' ')}" ออกจากตะกร้าแล้ว`); } function updateCartUI() { const countEl = document.getElementById('cart-count'); const itemsEl = document.getElementById('cart-items'); const totalEl = document.getElementById('cart-total'); const total = cart.reduce((s, i) => s + i.price * i.qty, 0); const totalQty = cart.reduce((s, i) => s + i.qty, 0); countEl.innerText = totalQty; if (cart.length === 0) { itemsEl.innerHTML = `<div class="cart-empty"><i class="fa-solid fa-bag-shopping"></i><p>ยังไม่มีสินค้าในตะกร้า</p></div>`; totalEl.innerText = '฿0'; return; } itemsEl.innerHTML = cart.map(item => ` <div class="cart-item"> <div class="cart-item-info"> <div class="cart-item-name">${item.name}</div> <div class="cart-item-sub"> ${item.size ? `<span class="cart-item-size">ไซส์ ${item.size}</span>` : ''} ฿${item.price.toLocaleString()} × ${item.qty} ชิ้น </div> </div> <div class="cart-item-right"> <span class="cart-item-price">฿${(item.price * item.qty).toLocaleString()}</span> <button class="cart-remove-btn" onclick="removeFromCart('${item.key}')" title="ลบสินค้า"> <i class="fa-regular fa-trash-can"></i> </button> </div> </div>`).join(''); totalEl.innerText = `฿${total.toLocaleString()}`; } /* ===== PAYMENT SYSTEM STATE & CONTROLLER ===== */ let currentPayMethod = 'promptpay'; let currentShippingSpeed = 'standard'; let currentDiscount = 0; let currentCouponCode = ''; let qrTimerCountdown = 899; // 14:59 in seconds let qrInterval = null; let selectedBankInfo = { name: 'กสิกรไทย (KBANK)', acc: '089-2-33412-1' }; function checkout() { openPaymentModal(); } function openPaymentModal() { // Check if user is logged in if (!currentUser) { showToast('🔒 กรุณาเข้าสู่ระบบก่อนทำการชำระเงินครับ'); openLoginModal(); return; } if (cart.length === 0) { showToast('🛒 กรุณาเลือกสินค้าลงตะกร้าก่อนทำการชำระเงิน'); return; } // Close cart panel if open if (document.getElementById('cart-panel').classList.contains('open')) { toggleCart(); } // Load user address from per-user profile const normEmail = currentUser.email.toLowerCase().trim(); const extras = JSON.parse(localStorage.getItem('horu_profile_' + normEmail) || '{}'); const username = currentUser.email.split('@')[0]; const nameEl = document.getElementById('pay-addr-name'); const phoneEl = document.getElementById('pay-addr-phone'); const detailEl = document.getElementById('pay-addr-detail'); if (nameEl) nameEl.textContent = extras.displayName || `คุณ ${username}`; if (phoneEl) phoneEl.textContent = extras.phone || '089-123-4567'; if (detailEl) detailEl.textContent = extras.address || '99/1 อาคาร HORU สุขุมวิท แขวงคลองเตย เขตคลองเตย กรุงเทพมหานคร 10110'; // Reset payment selections currentPayMethod = 'promptpay'; currentShippingSpeed = 'standard'; currentDiscount = 0; currentCouponCode = ''; const coupInp = document.getElementById('coupon-input'); if (coupInp) coupInp.value = ''; const discRow = document.getElementById('pay-discount-row'); if (discRow) discRow.style.display = 'none'; switchPayMethod('promptpay'); selectShipping('standard'); startQrTimer(); updatePaymentSummary(); document.getElementById('payment-modal').classList.add('show'); } function closePaymentModal(e) { if (!e || e.target === document.getElementById('payment-modal')) { document.getElementById('payment-modal').classList.remove('show'); if (qrInterval) clearInterval(qrInterval); } } function switchPayMethod(method) { currentPayMethod = method; const tabs = ['promptpay', 'card', 'bank', 'cod']; tabs.forEach(t => { const tabEl = document.getElementById('tab-pay-' + t); const contentEl = document.getElementById('pay-content-' + t); if (tabEl) tabEl.classList.toggle('active', t === method); if (contentEl) contentEl.style.display = t === method ? 'flex' : 'none'; }); if (method === 'promptpay') { startQrTimer(); } else if (qrInterval) { clearInterval(qrInterval); } } function selectShipping(speed) { currentShippingSpeed = speed; const stdCard = document.getElementById('ship-standard'); const expCard = document.getElementById('ship-express'); const radStd = document.getElementById('rad-ship-std'); const radExp = document.getElementById('rad-ship-exp'); if (speed === 'standard') { if (stdCard) stdCard.classList.add('active'); if (expCard) expCard.classList.remove('active'); if (radStd) radStd.checked = true; if (radExp) radExp.checked = false; } else { if (stdCard) stdCard.classList.remove('active'); if (expCard) expCard.classList.add('active'); if (radStd) radStd.checked = false; if (radExp) radExp.checked = true; } updatePaymentSummary(); } function startQrTimer() { if (qrInterval) clearInterval(qrInterval); qrTimerCountdown = 899; const timerEl = document.getElementById('qr-countdown'); qrInterval = setInterval(() => { qrTimerCountdown--; if (qrTimerCountdown <= 0) { clearInterval(qrInterval); if (timerEl) timerEl.textContent = '00:00 (หมดอายุ)'; return; } const mins = Math.floor(qrTimerCountdown / 60).toString().padStart(2, '0'); const secs = (qrTimerCountdown % 60).toString().padStart(2, '0'); if (timerEl) timerEl.textContent = `${mins}:${secs}`; }, 1000); } function selectBank(el, accNo, bankName) { document.querySelectorAll('.bank-option-card').forEach(b => b.classList.remove('active')); el.classList.add('active'); selectedBankInfo = { name: bankName, acc: accNo }; const disp = document.getElementById('selected-bank-display'); if (disp) disp.textContent = `${bankName}: ${accNo}`; showToast(`🏦 เลือก ${bankName}`); } function usePresetCoupon(code) { const input = document.getElementById('coupon-input'); if (input) input.value = code; applyCoupon(); } function applyCoupon() { const input = document.getElementById('coupon-input'); const code = input ? input.value.trim().toUpperCase() : ''; if (!code) { showToast('⚠️ กรุณาระบุรหัสโค้ดส่วนลด'); return; } const subtotal = cart.reduce((s, i) => s + i.price * i.qty, 0); if (code === 'HORU50') { currentDiscount = 50; currentCouponCode = 'HORU50'; showToast('🎉 ใช้โค้ด HORU50 สำเร็จ! รับส่วนลด ฿50'); } else if (code === 'FREESHIP') { currentDiscount = currentShippingSpeed === 'express' ? 45 : 0; currentCouponCode = 'FREESHIP'; showToast('🎉 ใช้โค้ด FREESHIP สำเร็จ! ฟรีค่าจัดส่งด่วน'); } else if (code === 'DISCOUNT10') { currentDiscount = Math.round(subtotal * 0.1); currentCouponCode = 'DISCOUNT10'; showToast(`🎉 ใช้โค้ด DISCOUNT10 สำเร็จ! ลด 10% (฿${currentDiscount.toLocaleString()})`); } else { showToast('❌ โค้ดส่วนลดไม่ถูกต้องหรือหมดอายุ'); return; } updatePaymentSummary(); } function formatCardNumber(input) { let val = input.value.replace(/\D/g, '').substring(0, 16); let formatted = val.match(/.{1,4}/g)?.join(' ') || val; input.value = formatted; const preview = document.getElementById('card-preview-num'); if (preview) preview.textContent = formatted || '•••• •••• •••• ••••'; } function updateCardName(input) { const preview = document.getElementById('card-preview-name'); if (preview) preview.textContent = input.value.toUpperCase() || 'YOUR NAME'; } function formatCardExpiry(input) { let val = input.value.replace(/\D/g, '').substring(0, 4); if (val.length >= 3) { input.value = val.substring(0, 2) + '/' + val.substring(2); } else { input.value = val; } const preview = document.getElementById('card-preview-exp'); if (preview) preview.textContent = input.value || 'MM/YY'; } function updatePaymentSummary() { const subtotal = cart.reduce((s, i) => s + i.price * i.qty, 0); const totalQty = cart.reduce((s, i) => s + i.qty, 0); let shippingFee = currentShippingSpeed === 'express' ? 45 : 0; if (currentCouponCode === 'FREESHIP') { currentDiscount = shippingFee; } const grandTotal = Math.max(0, subtotal + shippingFee - currentDiscount); const countEl = document.getElementById('pay-items-count'); const subtotalEl = document.getElementById('pay-subtotal'); const shipFeeEl = document.getElementById('pay-shipping-fee'); const discRow = document.getElementById('pay-discount-row'); const discName = document.getElementById('applied-coupon-name'); const discAmt = document.getElementById('pay-discount-amount'); const grandTotalEl = document.getElementById('pay-grand-total'); const btnLabel = document.getElementById('pay-btn-label'); const qrPayAmount = document.getElementById('qr-pay-amount'); if (countEl) countEl.textContent = totalQty; if (subtotalEl) subtotalEl.textContent = `฿${subtotal.toLocaleString()}`; if (shipFeeEl) shipFeeEl.textContent = shippingFee === 0 ? 'ฟรี' : `+฿${shippingFee}`; if (qrPayAmount) qrPayAmount.textContent = `฿${grandTotal.toLocaleString()}`; if (currentDiscount > 0) { if (discRow) discRow.style.display = 'flex'; if (discName) discName.textContent = currentCouponCode; if (discAmt) discAmt.textContent = `-฿${currentDiscount.toLocaleString()}`; } else { if (discRow) discRow.style.display = 'none'; } if (grandTotalEl) grandTotalEl.textContent = `฿${grandTotal.toLocaleString()}`; if (btnLabel) btnLabel.textContent = `ยืนยันการชำระเงิน ฿${grandTotal.toLocaleString()}`; // Update QR code url with real amount const qrImg = document.getElementById('promptpay-qr-img'); if (qrImg) { qrImg.src = `https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=PROMPTPAY-HORU-${grandTotal}`; } } function processPayment() { // Validate if card is selected if (currentPayMethod === 'card') { const cardNum = document.getElementById('card-inp-num').value.replace(/\s/g, ''); const cardName = document.getElementById('card-inp-name').value.trim(); const cardExp = document.getElementById('card-inp-exp').value.trim(); const cardCvv = document.getElementById('card-inp-cvv').value.trim(); if (cardNum.length < 16) { showToast('⚠️ กรุณากรอกหมายเลขบัตร 16 หลักให้ถูกต้อง'); return; } if (!cardName) { showToast('⚠️ กรุณากรอกชื่อบนหน้าบัตร'); return; } if (cardExp.length < 5) { showToast('⚠️ กรุณากรอกวันหมดอายุ MM/YY'); return; } if (cardCvv.length < 3) { showToast('⚠️ กรุณากรอกรหัส CVV 3 หลัก'); return; } } const btn = document.getElementById('pay-submit-btn'); btn.disabled = true; btn.innerHTML = `<i class="fa-solid fa-spinner fa-spin"></i> <span>กำลังตรวจสอบและชำระเงิน...</span>`; setTimeout(() => { btn.disabled = false; btn.innerHTML = `<i class="fa-solid fa-lock"></i> <span id="pay-btn-label">ยืนยันการชำระเงิน</span>`; const subtotal = cart.reduce((s, i) => s + i.price * i.qty, 0); const shippingFee = currentShippingSpeed === 'express' ? 45 : 0; const grandTotal = Math.max(0, subtotal + shippingFee - currentDiscount); const randomNum = Math.floor(10000 + Math.random() * 90000); const newOrderId = `ORU-2026-${randomNum}`; const newTrackingNo = `TH-HORU${Math.floor(10000000 + Math.random() * 90000000)}`; const methodLabels = { promptpay: 'พร้อมเพย์ QR Code', card: 'บัตรเครดิต/เดบิต', bank: `โอนผ่าน ${selectedBankInfo.name}`, cod: 'เก็บเงินปลายทาง (COD)' }; const orderItems = cart.map(item => { const prod = products.find(p => p.id === item.productId) || {}; return { name: item.name, size: item.size || 'Free', qty: item.qty, price: item.price, image: prod.image || 'https://images.unsplash.com/photo-1503342217505-b0a15ec3261c?w=100' }; }); const newOrder = { id: newOrderId, trackingNo: newTrackingNo, courier: currentShippingSpeed === 'express' ? 'HORU Express (จัดส่งด่วนพิเศษ)' : 'HORU Express', status: 'shipping', statusLabel: 'ชำระเงินสำเร็จ • เตรียมจัดส่ง', step: 2, date: '25 ส.ค. 2026', eta: currentShippingSpeed === 'express' ? 'พรุ่งนี้ ภายใน 24 ชม.' : 'ภายใน 2-3 วันทำการ', payMethod: methodLabels[currentPayMethod] || 'พร้อมเพย์', driver: { name: 'คุณกิตติศักดิ์ ว่องไว', phone: '085-333-8899', vehicle: 'ฮอนด้า คลิก 160 (5กง-3321 กทม.)' }, timeline: [ { time: 'เมื่อสักครู่', desc: `ชำระเงินสำเร็จผ่าน ${methodLabels[currentPayMethod]} ยอดสุทธิ ฿${grandTotal.toLocaleString()}`, location: 'ระบบชำระเงิน HORU Pay', active: true }, { time: 'เมื่อสักครู่', desc: 'ร้านค้ากำลังเตรียมบรรจุสินค้าและพิมพ์ใบปะหน้าพัสดุ', location: 'HORU Central Warehouse', active: false } ], items: orderItems, total: grandTotal }; // Add to order list sampleOrders.unshift(newOrder); saveUserOrders(); // Clear cart & close payment modal cart = []; saveUserCart(); updateCartUI(); recordUserActivity('สั่งซื้อและชำระเงิน', `ออเดอร์ #${newOrderId} (฿${grandTotal.toLocaleString()})`); document.getElementById('payment-modal').classList.remove('show'); if (qrInterval) clearInterval(qrInterval); showToast(`🎉 ชำระเงินสำเร็จ ฿${grandTotal.toLocaleString()}! ออเดอร์ #${newOrderId}`); // Open tracking modal for the new order setTimeout(() => { openTrackingModal(newOrderId); }, 500); }, 1200); } /* ===== Toast Notification ===== */ let toastTimer; function showToast(msg) { const t = document.getElementById('toast'); document.getElementById('toast-msg').innerText = msg; t.classList.add('show'); clearTimeout(toastTimer); toastTimer = setTimeout(() => t.classList.remove('show'), 2800); } /* ===== Search and Filter Logic ===== */ function handleSearch() { searchQuery = document.getElementById('search-input').value.toLowerCase(); filterProducts(); } function setSearchTag(tag) { document.getElementById('search-input').value = tag; searchQuery = tag.toLowerCase(); filterProducts(); } function filterByRating(minRating) { currentRatingFilter = minRating; filterProducts(); } function filterProducts() { let filtered = products.filter(p => { const matchesSearch = p.name.toLowerCase().includes(searchQuery) || p.brand.toLowerCase().includes(searchQuery) || (p.category && p.category.toLowerCase().includes(searchQuery)); return matchesSearch; }); // Apply sorting if (currentSort === 'popular') { filtered.sort((a, b) => b.rating - a.rating); } else if (currentSort === 'latest') { filtered.sort((a, b) => a.dateAdded - b.dateAdded); } else if (currentSort === 'sales') { filtered.sort((a, b) => b.salesCount - a.salesCount); } const priceSortEl = document.getElementById('price-sort'); const priceSortVal = priceSortEl ? priceSortEl.value : 'none'; if (priceSortVal === 'asc') { filtered.sort((a, b) => a.price - b.price); } else if (priceSortVal === 'desc') { filtered.sort((a, b) => b.price - a.price); } renderProducts(filtered); } function setSort(type) { document.querySelectorAll('.sort-btn').forEach(btn => btn.classList.remove('active')); const popBtn = document.getElementById('sort-popular'); const latBtn = document.getElementById('sort-latest'); const salBtn = document.getElementById('sort-sales'); if (type === 'popular' && popBtn) popBtn.classList.add('active'); if (type === 'latest' && latBtn) latBtn.classList.add('active'); if (type === 'sales' && salBtn) salBtn.classList.add('active'); currentSort = type; filterProducts(); } function handlePriceSort() { filterProducts(); } // On Page Load window.addEventListener('DOMContentLoaded', () => { checkUserSession(); renderProducts(); }); </script> </body> </html>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.28 |
proxy
|
phpinfo
|
Settings