File manager - Edit - /home/webapp69.cm.in.th/u69319090046/02_shop/index.php
Back
<?php require_once __DIR__ . '/config.php'; // Helper for file uploads (Avatar, Product, Slip) function handleFileUpload($fileArray, $subfolder = 'avatars') { if (!isset($fileArray) || $fileArray['error'] !== UPLOAD_ERR_OK) { return ['success' => false, 'message' => 'ไม่มีไฟล์ถูกอัปโหลดหรือเกิดข้อผิดพลาด']; } $allowedMimes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/jpg']; $fileMime = mime_content_type($fileArray['tmp_name']); if (!in_array($fileMime, $allowedMimes)) { return ['success' => false, 'message' => 'รองรับเฉพาะไฟล์รูปภาพ (JPG, PNG, WEBP, GIF) เท่านั้น']; } $ext = pathinfo($fileArray['name'], PATHINFO_EXTENSION); if (!$ext) $ext = 'jpg'; $fileName = time() . '_' . rand(1000, 9999) . '.' . strtolower($ext); $targetDir = UPLOADS_DIR . '/' . $subfolder; if (!file_exists($targetDir)) { mkdir($targetDir, 0777, true); } $targetPath = $targetDir . '/' . $fileName; if (move_uploaded_file($fileArray['tmp_name'], $targetPath)) { $publicUrl = 'uploads/' . $subfolder . '/' . $fileName; return ['success' => true, 'url' => $publicUrl]; } return ['success' => false, 'message' => 'ไม่สามารถบันทึกไฟล์รูปภาพได้']; } // Helper for base64 image uploads function handleBase64Upload($base64Data, $subfolder = 'avatars') { if (empty($base64Data) || !preg_match('/^data:image\/(\w+);base64,/', $base64Data, $type)) { return ['success' => false, 'message' => 'ข้อมูลรูปภาพไม่ถูกต้อง']; } $data = substr($base64Data, strpos($base64Data, ',') + 1); $ext = strtolower($type[1]); if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) { $ext = 'png'; } $decoded = base64_decode($data); if ($decoded === false) { return ['success' => false, 'message' => 'การถอดรหัสรูปภาพล้มเหลว']; } $fileName = time() . '_' . rand(1000, 9999) . '.' . $ext; $targetDir = UPLOADS_DIR . '/' . $subfolder; if (!file_exists($targetDir)) { mkdir($targetDir, 0777, true); } $targetPath = $targetDir . '/' . $fileName; if (file_put_contents($targetPath, $decoded)) { $publicUrl = 'uploads/' . $subfolder . '/' . $fileName; return ['success' => true, 'url' => $publicUrl]; } return ['success' => false, 'message' => 'ไม่สามารถบันทึกไฟล์รูปภาพได้']; } // Check JSON Payload or multipart POST $rawInput = file_get_contents('php://input'); $jsonInput = json_decode($rawInput, true); // --- BACKEND API ENDPOINTS --- if ($_SERVER['REQUEST_METHOD'] === 'POST') { header('Content-Type: application/json; charset=utf-8'); $data = $jsonInput ?: $_POST; $action = $data['action'] ?? ''; // Action: Member Registration if ($action === 'register') { $res = registerUser( $data['username'] ?? '', $data['email'] ?? '', $data['password'] ?? '', $data['fullName'] ?? '', $data['phone'] ?? '', $data['address'] ?? '' ); if (!$res['success']) { http_response_code(400); } echo json_encode($res, JSON_UNESCAPED_UNICODE); exit; } // Action: Member Login if ($action === 'login') { $res = loginUser($data['username'] ?? '', $data['password'] ?? ''); if (!$res['success']) { http_response_code(400); } echo json_encode($res, JSON_UNESCAPED_UNICODE); exit; } // Action: 1-Click Quick Demo Login if ($action === 'quick_login') { $tier = trim($data['tier'] ?? 'classic'); $res = quickLoginAsTier($tier); if (!$res['success']) { http_response_code(400); } echo json_encode($res, JSON_UNESCAPED_UNICODE); exit; } // Action: Member Logout if ($action === 'logout') { unset($_SESSION['user']); echo json_encode(['success' => true, 'message' => 'ออกจากระบบเรียบร้อยแล้ว'], JSON_UNESCAPED_UNICODE); exit; } // Action: Claim Daily Shopee Coins (7-Day Streak) if ($action === 'claim_daily_coins') { $user = getLoggedInUser(); if (!$user) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'กรุณาเข้าสู่ระบบก่อนรับเหรียญ'], JSON_UNESCAPED_UNICODE); exit; } $res = claimDailyCoinsWithStreak($user['id']); if (!$res['success']) { http_response_code(400); } echo json_encode($res, JSON_UNESCAPED_UNICODE); exit; } // Action: Lucky Spin Wheel if ($action === 'lucky_spin') { $user = getLoggedInUser(); if (!$user) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'กรุณาเข้าสู่ระบบก่อนหมุนวงล้อ'], JSON_UNESCAPED_UNICODE); exit; } $res = spinLuckyWheel($user['id']); if (!$res['success']) { http_response_code(400); } echo json_encode($res, JSON_UNESCAPED_UNICODE); exit; } // Action: Claim Mission Reward if ($action === 'claim_mission') { $user = getLoggedInUser(); if (!$user) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'กรุณาเข้าสู่ระบบก่อน'], JSON_UNESCAPED_UNICODE); exit; } $missionId = trim($data['missionId'] ?? ''); $res = claimMissionReward($user['id'], $missionId); if (!$res['success']) { http_response_code(400); } echo json_encode($res, JSON_UNESCAPED_UNICODE); exit; } // Action: Collect Voucher if ($action === 'collect_voucher') { $user = getLoggedInUser(); if (!$user) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'กรุณาเข้าสู่ระบบก่อนเก็บโค้ด'], JSON_UNESCAPED_UNICODE); exit; } $code = trim($data['voucherCode'] ?? ''); if (empty($code)) { http_response_code(400); echo json_encode(['success' => false, 'message' => 'รหัสโค้ดไม่ถูกต้อง'], JSON_UNESCAPED_UNICODE); exit; } $res = collectVoucher($user['id'], $code); echo json_encode($res, JSON_UNESCAPED_UNICODE); exit; } // Action: Update User Profile & Avatar if ($action === 'update_profile') { $user = getLoggedInUser(); if (!$user) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'กรุณาเข้าสู่ระบบก่อน'], JSON_UNESCAPED_UNICODE); exit; } $pdo = getDbConnection(); $fullName = trim($data['fullName'] ?? ''); $phone = trim($data['phone'] ?? ''); $address = trim($data['address'] ?? ''); $gender = trim($data['gender'] ?? 'other'); $birthday = trim($data['birthday'] ?? ''); $avatarUrl = trim($data['avatar'] ?? $user['avatar']); if (isset($_FILES['avatar_file'])) { $uploadRes = handleFileUpload($_FILES['avatar_file'], 'avatars'); if ($uploadRes['success']) { $avatarUrl = $uploadRes['url']; } } elseif (!empty($data['avatar_base64'])) { $uploadRes = handleBase64Upload($data['avatar_base64'], 'avatars'); if ($uploadRes['success']) { $avatarUrl = $uploadRes['url']; } } if ($pdo && !empty($fullName) && !empty($phone)) { $stmt = $pdo->prepare("UPDATE users SET full_name = ?, phone = ?, address = ?, gender = ?, birthday = ?, avatar = ? WHERE id = ?"); $stmt->execute([$fullName, $phone, $address, $gender, $birthday, $avatarUrl, $user['id']]); $stmtUser = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmtUser->execute([$user['id']]); $updatedUser = formatUserData($stmtUser->fetch()); $_SESSION['user'] = $updatedUser; echo json_encode(['success' => true, 'message' => 'อัปเดตข้อมูลโปรไฟล์ Shopee สำเร็จแล้ว', 'user' => $updatedUser], JSON_UNESCAPED_UNICODE); } else { http_response_code(400); echo json_encode(['success' => false, 'message' => 'ข้อมูลไม่ถูกต้อง กรุณาระบุชื่อและเบอร์โทร'], JSON_UNESCAPED_UNICODE); } exit; } // Action: Upload Avatar Directly if ($action === 'upload_avatar') { $user = getLoggedInUser(); if (!$user) { http_response_code(401); echo json_encode(['success' => false, 'message' => 'กรุณาเข้าสู่ระบบก่อน'], JSON_UNESCAPED_UNICODE); exit; } $avatarUrl = ''; if (isset($_FILES['avatar_file'])) { $uploadRes = handleFileUpload($_FILES['avatar_file'], 'avatars'); if ($uploadRes['success']) { $avatarUrl = $uploadRes['url']; } else { http_response_code(400); echo json_encode($uploadRes, JSON_UNESCAPED_UNICODE); exit; } } elseif (!empty($data['avatar_base64'])) { $uploadRes = handleBase64Upload($data['avatar_base64'], 'avatars'); if ($uploadRes['success']) { $avatarUrl = $uploadRes['url']; } else { http_response_code(400); echo json_encode($uploadRes, JSON_UNESCAPED_UNICODE); exit; } } if ($avatarUrl) { $pdo = getDbConnection(); $stmt = $pdo->prepare("UPDATE users SET avatar = ? WHERE id = ?"); $stmt->execute([$avatarUrl, $user['id']]); $stmtUser = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmtUser->execute([$user['id']]); $updatedUser = formatUserData($stmtUser->fetch()); $_SESSION['user'] = $updatedUser; echo json_encode(['success' => true, 'message' => 'เปลี่ยนรูปโปรไฟล์สำเร็จ!', 'user' => $updatedUser, 'avatar' => $avatarUrl], JSON_UNESCAPED_UNICODE); exit; } http_response_code(400); echo json_encode(['success' => false, 'message' => 'ไม่มีไฟล์ถูกอัปโหลด'], JSON_UNESCAPED_UNICODE); exit; } // Action: Add Product with Image if ($action === 'add_product') { $name = trim($data['name'] ?? ''); $category = trim($data['category'] ?? 't-shirts'); $categoryName = trim($data['category_name'] ?? 'เสื้อผ้าแฟชั่น'); $subtitle = trim($data['subtitle'] ?? ''); $description = trim($data['description'] ?? ''); $price = floatval($data['price'] ?? 390); $originalPrice = floatval($data['original_price'] ?? ($price * 1.5)); $badge = trim($data['badge'] ?? 'NEW ARRIVAL'); $badgeColor = trim($data['badge_color'] ?? 'bg-emerald-600'); $sizes = $data['sizes'] ?? ['S', 'M', 'L', 'XL', '2XL']; if (is_string($sizes)) { $sizes = array_map('trim', explode(',', $sizes)); } $imageUrl = 'https://images.unsplash.com/photo-1521572267360-ee0c2909d518?w=800&auto=format&fit=crop&q=80'; if (isset($_FILES['product_image'])) { $uploadRes = handleFileUpload($_FILES['product_image'], 'products'); if ($uploadRes['success']) { $imageUrl = $uploadRes['url']; } } elseif (!empty($data['image_base64'])) { $uploadRes = handleBase64Upload($data['image_base64'], 'products'); if ($uploadRes['success']) { $imageUrl = $uploadRes['url']; } } elseif (!empty($data['image_url'])) { $imageUrl = trim($data['image_url']); } if (empty($name) || $price <= 0) { http_response_code(400); echo json_encode(['success' => false, 'message' => 'กรุณาระบุชื่อสินค้าและราคาที่ถูกต้อง'], JSON_UNESCAPED_UNICODE); exit; } $productId = 'custom-' . time() . '-' . rand(100, 999); $newProduct = [ 'id' => $productId, 'category' => $category, 'category_name' => $categoryName, 'name' => $name, 'subtitle' => $subtitle ?: 'สินค้าแฟชั่นสตรีทพรีเมียม APEX STUDIO', 'price' => $price, 'original_price' => $originalPrice, 'sold_count' => rand(10, 50), 'badge' => $badge, 'badge_color' => $badgeColor, 'rating' => 5.0, 'reviews_count' => rand(5, 20), 'image' => $imageUrl, 'gallery' => [$imageUrl], 'colors' => [ ['id' => 'custom-black', 'name' => 'Classic Black', 'hex' => '#18181b', 'collarHex' => '#09090b', 'textDark' => false, 'printColor' => '#ffffff'], ['id' => 'custom-white', 'name' => 'Off-White', 'hex' => '#f5f5f4', 'collarHex' => '#e7e5e4', 'textDark' => true, 'printColor' => '#0f172a'] ], 'sizes' => $sizes, 'description' => $description ?: 'สินค้าแฟชั่นสตรีทแวร์คุณภาพสูง ดีไซน์พรีเมียม สวมใส่สบาย' ]; saveCustomProduct($newProduct); echo json_encode([ 'success' => true, 'message' => 'เพิ่มสินค้าใหม่และอัปโหลดรูปภาพสำเร็จแล้ว!', 'product' => $newProduct, 'allProducts' => getAllProducts() ], JSON_UNESCAPED_UNICODE); exit; } // Action: Upload Payment Slip if ($action === 'upload_slip') { $orderId = trim($data['orderId'] ?? ''); $slipUrl = ''; if (isset($_FILES['slip_image'])) { $uploadRes = handleFileUpload($_FILES['slip_image'], 'slips'); if ($uploadRes['success']) { $slipUrl = $uploadRes['url']; } } elseif (!empty($data['slip_base64'])) { $uploadRes = handleBase64Upload($data['slip_base64'], 'slips'); if ($uploadRes['success']) { $slipUrl = $uploadRes['url']; } } if (!$slipUrl) { http_response_code(400); echo json_encode(['success' => false, 'message' => 'ไม่พบไฟล์สลิปหรืออัปโหลดล้มเหลว'], JSON_UNESCAPED_UNICODE); exit; } $pdo = getDbConnection(); if ($pdo && !empty($orderId)) { $stmt = $pdo->prepare("UPDATE orders SET slip_image = ?, status = 'Slip Uploaded' WHERE order_id = ?"); $stmt->execute([$slipUrl, $orderId]); } $orders = getAllOrders(); foreach ($orders as &$ord) { if ($ord['orderId'] === $orderId) { $ord['slipImage'] = $slipUrl; $ord['status'] = 'Slip Uploaded'; break; } } file_put_contents(ORDERS_FILE, json_encode($orders, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); echo json_encode(['success' => true, 'message' => 'อัปโหลดสลิปโอนเงินสำเร็จ! เจ้าหน้าที่จะตรวจสอบยอดชำระ', 'slipUrl' => $slipUrl], JSON_UNESCAPED_UNICODE); exit; } // Action: Submit New Order / Pre-Order with Shopee Coins & Vouchers $fullName = trim($data['fullName'] ?? ''); $phone = trim($data['phone'] ?? ''); $address = trim($data['address'] ?? ''); $note = trim($data['note'] ?? ''); $paymentMethod = trim($data['paymentMethod'] ?? 'promptpay'); $items = $data['items'] ?? []; $coinsToUse = max(0, intval($data['coinsUsed'] ?? 0)); $voucherCode = trim($data['voucherCode'] ?? ''); $errors = []; if (empty($fullName)) { $errors[] = 'กรุณากรอกชื่อ-นามสกุล'; } $rawPhone = preg_replace('/\D/', '', $phone); if (empty($rawPhone) || strlen($rawPhone) !== 10 || !preg_match('/^(06|08|09)/', $rawPhone)) { $errors[] = 'เบอร์โทรศัพท์ต้องมี 10 หลัก ขึ้นต้นด้วย 06, 08 หรือ 09'; } if (empty($address) || strlen($address) < 10) { $errors[] = 'กรุณากรอกที่อยู่จัดส่งสินค้าให้ครบถ้วน'; } if (empty($items) || !is_array($items)) { $errors[] = 'ไม่มีรายการสินค้าในตะกร้า'; } $currentRemaining = getRemainingStock(); $totalRequestedQty = 0; foreach ($items as $item) { $totalRequestedQty += max(1, intval($item['quantity'] ?? 1)); } if ($totalRequestedQty > $currentRemaining) { $errors[] = "ขออภัย สินค้าคงเหลือเพียง {$currentRemaining} ชิ้น ไม่พอสำหรับจำนวนที่สั่งซื้อ ({$totalRequestedQty} ชิ้น)"; } if (!empty($errors)) { http_response_code(422); echo json_encode(['success' => false, 'message' => implode(' | ', $errors)], JSON_UNESCAPED_UNICODE); exit; } $subtotal = 0; $totalQty = 0; $validatedItems = []; foreach ($items as $item) { $qty = max(1, intval($item['quantity'] ?? 1)); $itemPrice = floatval($item['price'] ?? PRODUCT_PRICE); $itemSubtotal = $itemPrice * $qty; $subtotal += $itemSubtotal; $totalQty += $qty; $validatedItems[] = [ 'title' => $item['title'] ?? 'Apex Streetwear Item', 'color' => $item['color'] ?? [], 'size' => $item['size'] ?? [], 'price' => $itemPrice, 'quantity' => $qty, 'subtotal' => $itemSubtotal, 'image' => $item['image'] ?? '' ]; } $shippingCost = ($totalQty >= FREE_SHIPPING_MIN_QTY) ? 0 : SHIPPING_FEE; // Voucher calculation $voucherDiscount = 0; if ($voucherCode === 'SHOPEEFREE') { $voucherDiscount = $shippingCost; $shippingCost = 0; } elseif ($voucherCode === 'MALL10') { $voucherDiscount = round($subtotal * 0.10); } elseif ($voucherCode === 'NEWUSER50') { $voucherDiscount = min(50, $subtotal); } elseif ($voucherCode === 'BDAY100') { $voucherDiscount = min(100, $subtotal); } $codFee = ($paymentMethod === 'cod') ? 20 : 0; $promoDiscount = intval($data['discount'] ?? 0); $totalDiscount = $voucherDiscount + $promoDiscount; // Coins deduction (1 Coin = 1 THB) $loggedInUser = getLoggedInUser(); $maxCoinsAvailable = $loggedInUser ? intval($loggedInUser['coins'] ?? 0) : 0; $actualCoinsUsed = min($coinsToUse, $maxCoinsAvailable, max(0, $subtotal + $shippingCost + $codFee - $totalDiscount)); $grandTotal = max(0, $subtotal + $shippingCost + $codFee - $totalDiscount - $actualCoinsUsed); // Coins earned calculation (Shopee Cashback e.g. 1% - 10%) $cashbackRate = 0.05; if ($loggedInUser && isset($loggedInUser['tier_info']['coinCashback'])) { $cashbackRate = floatval($loggedInUser['tier_info']['coinCashback']) / 100; } if ($voucherCode === 'COINBACK20') { $cashbackRate = 0.20; } $coinsEarned = max(1, round($grandTotal * $cashbackRate)); $formattedPhone = sprintf('%s-%s-%s', substr($rawPhone, 0, 3), substr($rawPhone, 3, 3), substr($rawPhone, 6)); $orderRef = 'SHP-' . date('Ymd') . '-' . rand(1000, 9999); $paymentLabel = ($paymentMethod === 'cod') ? 'เก็บเงินปลายทาง (COD)' : 'Mobile Banking (Thai QR PromptPay)'; $newOrder = [ 'orderId' => $orderRef, 'userId' => $loggedInUser ? $loggedInUser['id'] : null, 'customer' => [ 'fullName' => $fullName, 'phone' => $formattedPhone, 'rawPhone' => $rawPhone, 'address' => $address, 'note' => $note, 'paymentMethod' => $paymentMethod, 'paymentLabel' => $paymentLabel ], 'items' => $validatedItems, 'subtotal' => $subtotal, 'shipping' => $shippingCost, 'codFee' => $codFee, 'discount' => $totalDiscount, 'voucherCode' => $voucherCode, 'coinsUsed' => $actualCoinsUsed, 'coinsEarned' => $coinsEarned, 'grandTotal' => $grandTotal, 'status' => ($paymentMethod === 'cod') ? 'Pending COD' : 'Pending Payment', 'slipImage' => '', 'trackingNumber' => 'TH' . rand(10000000, 99999999) . 'EX', 'createdAt' => date('Y-m-d H:i:s'), 'estimatedDelivery' => '25 สิงหาคม 2026' ]; $existingOrders = getAllOrders(); array_unshift($existingOrders, $newOrder); file_put_contents(ORDERS_FILE, json_encode($existingOrders, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)); // Save to database saveOrderToDb($newOrder, $loggedInUser ? $loggedInUser['id'] : null); // Refresh logged in user $freshUser = getLoggedInUser(); echo json_encode([ 'success' => true, 'order' => $newOrder, 'user' => $freshUser, 'totalOrdered' => getTotalOrderedCount(), 'remainingStock' => getRemainingStock(), 'allOrders' => $existingOrders ], JSON_UNESCAPED_UNICODE); exit; } if ($_SERVER['REQUEST_METHOD'] === 'GET' && (isset($_GET['api']) || isset($_GET['action']) || isset($_GET['search']))) { header('Content-Type: application/json; charset=utf-8'); $action = $_GET['action'] ?? ''; if ($action === 'get_orders') { echo json_encode(['success' => true, 'orders' => getAllOrders()], JSON_UNESCAPED_UNICODE); exit; } if ($action === 'get_user') { $user = getLoggedInUser(); $userOrders = $user ? getUserOrdersFromDb($user['id']) : []; $coinLogs = $user ? getCoinLogs($user['id'], 30) : []; echo json_encode([ 'success' => true, 'user' => $user, 'orders' => $userOrders, 'coinLogs' => $coinLogs, 'tiers' => $SHOPEE_TIERS, 'vouchers' => $AVAILABLE_VOUCHERS, 'missions' => $SHOPEE_MISSIONS, 'streakRewards' => $DAILY_STREAK_REWARDS ], JSON_UNESCAPED_UNICODE); exit; } if ($action === 'get_products') { echo json_encode(['success' => true, 'products' => getAllProducts()], JSON_UNESCAPED_UNICODE); exit; } $search = trim($_GET['search'] ?? ''); if (!empty($search)) { $cleanSearch = preg_replace('/\D/', '', $search); $orders = getAllOrders(); $matched = []; foreach ($orders as $order) { if ( $order['orderId'] === $search || (isset($order['customer']['rawPhone']) && $order['customer']['rawPhone'] === $cleanSearch) || (isset($order['customer']['phone']) && str_replace('-', '', $order['customer']['phone']) === $cleanSearch) ) { $matched[] = $order; } } echo json_encode(['success' => true, 'orders' => $matched], JSON_UNESCAPED_UNICODE); exit; } echo json_encode([ 'success' => true, 'totalOrdered' => getTotalOrderedCount(), 'remainingStock' => getRemainingStock(), 'orders' => getAllOrders(), 'products' => getAllProducts() ], JSON_UNESCAPED_UNICODE); exit; } // Initial Data for Frontend $allOrdersData = getAllOrders(); $initialTotalOrdered = getTotalOrderedCount(); $initialRemainingStock = getRemainingStock(); $currentUser = getLoggedInUser(); $allProducts = getAllProducts(); ?> <!DOCTYPE html> <html lang="th" class="dark scroll-smooth"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <title>Shopee Mall x APEX STUDIO - ระบบสมาชิก VIP Club & แฟชั่นสตรีท</title> <meta name="title" content="Shopee Mall x APEX STUDIO - ระบบสมาชิก Shopee VIP"> <meta name="description" content="ระบบสมาชิก Shopee VIP Club รับ Shopee Coins, เช็คอิน 7 วัน, วงล้อหมุนลุ้นโชค, ภารกิจสะสมเหรียญ และคูปองส่งฟรี"> <meta name="theme-color" content="#ee4d2d"> <!-- Tailwind CSS CDN --> <script src="https://cdn.tailwindcss.com"></script> <script> tailwind.config = { darkMode: 'class', theme: { extend: { colors: { shopee: { DEFAULT: '#ee4d2d', dark: '#d03b1a', light: '#ff6433', gold: '#f6a700', bg: '#0f172a' } }, fontFamily: { sans: ['Plus Jakarta Sans', 'Prompt', 'sans-serif'], } } } } </script> <!-- Google Fonts --> <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=Plus+Jakarta+Sans:wght@400;500;600;700;800;900&family=Prompt: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"> <!-- Embedded Custom CSS --> <style> :root { --font-sans: 'Plus Jakarta Sans', 'Prompt', sans-serif; } body { font-family: var(--font-sans); background-color: #080d1a; color: #f8fafc; overflow-x: hidden; } /* Custom Scrollbar */ ::-webkit-scrollbar { width: 7px; height: 7px; } ::-webkit-scrollbar-track { background: #080d1a; } ::-webkit-scrollbar-thumb { background: #1e293b; border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #ee4d2d; } .glass-panel { background: rgba(15, 23, 42, 0.78); backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px); border: 1px solid rgba(255, 255, 255, 0.08); } .glass-card { background: rgba(18, 26, 47, 0.65); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.07); } .shopee-gradient { background: linear-gradient(135deg, #ff5722 0%, #ee4d2d 50%, #d03b1a 100%); } .shopee-gold-gradient { background: linear-gradient(135deg, #ffd700 0%, #f6a700 50%, #d48800 100%); } .shopee-vip-gradient { background: linear-gradient(135deg, #8b5cf6 0%, #6366f1 50%, #4338ca 100%); } .hologram-card { position: relative; background-size: 200% 200%; box-shadow: 0 20px 40px -15px rgba(0,0,0,0.6); transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); } .hologram-card::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(125deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.02) 40%, rgba(255,255,255,0.3) 60%, rgba(255,255,255,0) 100%); pointer-events: none; border-radius: inherit; } @keyframes spinWheel { from { transform: rotate(0deg); } to { transform: rotate(1800deg); } } @keyframes pulseGlow { 0%, 100% { opacity: 0.85; transform: scale(1); } 50% { opacity: 1; transform: scale(1.02); } } .animate-pulse-glow { animation: pulseGlow 2.5s ease-in-out infinite; } @keyframes slideInRight { from { transform: translateX(100%); } to { transform: translateX(0); } } .animate-slide-in-right { animation: slideInRight 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; } @keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } .animate-fade-in { animation: fadeIn 0.25s cubic-bezier(0.16, 1, 0.3, 1) forwards; } .touch-target { min-height: 44px; min-width: 44px; } </style> <!-- React 18 UMD & Babel Standalone --> <script src="https://unpkg.com/react@18/umd/react.production.min.js" crossorigin></script> <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" crossorigin></script> <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <!-- Initial PHP Data --> <script> window.PHP_STORE_CONFIG = { storeName: "<?php echo STORE_NAME; ?>", productName: "<?php echo PRODUCT_NAME; ?>", productPrice: <?php echo PRODUCT_PRICE; ?>, shippingFee: <?php echo SHIPPING_FEE; ?>, freeShippingMinQty: <?php echo FREE_SHIPPING_MIN_QTY; ?>, campaignGoal: <?php echo TOTAL_STOCK_LIMIT; ?>, initialOrdered: <?php echo INITIAL_ORDER_OFFSET; ?>, totalOrdered: <?php echo $initialTotalOrdered; ?>, remainingStock: <?php echo $initialRemainingStock; ?>, adminPassword: "<?php echo ADMIN_PASSWORD; ?>" }; window.PHP_COLOR_OPTIONS = <?php echo json_encode($COLOR_OPTIONS, JSON_UNESCAPED_UNICODE); ?>; window.PHP_SIZE_OPTIONS = <?php echo json_encode($SIZE_OPTIONS, JSON_UNESCAPED_UNICODE); ?>; window.PHP_INITIAL_ORDERS = <?php echo json_encode($allOrdersData, JSON_UNESCAPED_UNICODE); ?>; window.PHP_USER = <?php echo json_encode($currentUser, JSON_UNESCAPED_UNICODE); ?>; window.PHP_FASHION_CATALOG = <?php echo json_encode($allProducts, JSON_UNESCAPED_UNICODE); ?>; window.PHP_SHOPEE_TIERS = <?php echo json_encode($SHOPEE_TIERS, JSON_UNESCAPED_UNICODE); ?>; window.PHP_AVAILABLE_VOUCHERS = <?php echo json_encode($AVAILABLE_VOUCHERS, JSON_UNESCAPED_UNICODE); ?>; window.PHP_SHOPEE_MISSIONS = <?php echo json_encode($SHOPEE_MISSIONS, JSON_UNESCAPED_UNICODE); ?>; window.PHP_STREAK_REWARDS = <?php echo json_encode($DAILY_STREAK_REWARDS, JSON_UNESCAPED_UNICODE); ?>; </script> </head> <body class="bg-[#080d1a] text-slate-100 antialiased min-h-screen"> <div id="root"> <div class="min-h-screen flex flex-col items-center justify-center space-y-4 bg-[#080d1a] text-white"> <div class="w-14 h-14 rounded-2xl shopee-gradient animate-bounce flex items-center justify-center shadow-lg shadow-orange-500/30"> <span class="text-white font-black text-2xl">S</span> </div> <p class="text-xs font-bold text-orange-400 tracking-wider">กำลังโหลด SHOPEE MALL x APEX STUDIO VIP CLUB...</p> </div> </div> <script type="text/babel"> const { useState, useEffect, useMemo, useCallback, useRef } = React; const STORE_CONFIG = window.PHP_STORE_CONFIG || {}; const COLOR_OPTIONS = window.PHP_COLOR_OPTIONS || []; const SIZE_OPTIONS = window.PHP_SIZE_OPTIONS || []; const SHOPEE_TIERS = window.PHP_SHOPEE_TIERS || {}; const AVAILABLE_VOUCHERS = window.PHP_AVAILABLE_VOUCHERS || []; const SHOPEE_MISSIONS = window.PHP_SHOPEE_MISSIONS || []; const STREAK_REWARDS = window.PHP_STREAK_REWARDS || {}; // --- ICONS --- const Icons = { Shopee: ({ className = "w-5 h-5" }) => ( <svg className={className} viewBox="0 0 24 24" fill="currentColor"> <path d="M19.5 8H16V6.5C16 4.01 13.99 2 11.5 2S7 4.01 7 6.5V8H3.5C2.67 8 2 8.67 2 9.5l1.25 10.5C3.36 20.89 4.19 21.7 5.09 21.7h12.82c.9 0 1.73-.81 1.84-1.7L21 9.5c0-.83-.67-1.5-1.5-1.5zM9 6.5C9 5.12 10.12 4 11.5 4S14 5.12 14 6.5V8H9V6.5zm7.3 10.15c-.24.77-.96 1.35-1.92 1.35-.91 0-1.63-.52-1.9-1.28l1.1-.42c.13.41.45.67.8.67.43 0 .78-.28.78-.63 0-.31-.19-.48-.73-.67l-.67-.24c-.95-.34-1.42-.92-1.42-1.7 0-1.12.91-1.89 2.04-1.89.9 0 1.58.48 1.84 1.23l-1.07.45c-.14-.38-.41-.62-.77-.62-.39 0-.69.25-.69.58 0 .28.18.45.63.6l.66.23c1.07.38 1.56.96 1.56 1.78 0 .02-.01.37-.24.59z" /> </svg> ), Coins: ({ className = "w-5 h-5" }) => ( <svg className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2}> <circle cx="9" cy="9" r="7" stroke="currentColor" fill="currentColor" fillOpacity="0.15" /> <path d="M15 9a7 7 0 11-7 7" strokeLinecap="round" /> <path d="M9 7v4m-2-2h4" strokeLinecap="round" /> </svg> ), ShoppingBag: ({ className = "w-5 h-5" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z" /> </svg> ), Sparkles: ({ className = "w-5 h-5" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" /> </svg> ), Truck: ({ className = "w-5 h-5" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z" /> <path strokeLinecap="round" strokeLinejoin="round" d="M13 16V6a1 1 0 00-1-1H4a1 1 0 00-1 1v10a1 1 0 001 1h1m8-1a1 1 0 01-1 1H9m4-1e1 1h4.586a1 1 0 01.707.293l2.414 2.414a1 1 0 01.293.707V16a1 1 0 01-1 1h-1m-6-1a1 1 0 001 1h1M5 17a2 2 0 100-4 2 2 0 000 4zm10 0a2 2 0 100-4 2 2 0 000 4z" /> </svg> ), Check: ({ className = "w-5 h-5" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}> <path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" /> </svg> ), Plus: ({ className = "w-4 h-4" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}> <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v16m8-8H4" /> </svg> ), Minus: ({ className = "w-4 h-4" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}> <path strokeLinecap="round" strokeLinejoin="round" d="M20 12H4" /> </svg> ), Trash: ({ className = "w-4 h-4" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" /> </svg> ), X: ({ className = "w-5 h-5" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" /> </svg> ), Upload: ({ className = "w-5 h-5" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" /> </svg> ), Image: ({ className = "w-5 h-5" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> </svg> ), User: ({ className = "w-4 h-4" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /> </svg> ), Tag: ({ className = "w-4 h-4" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" /> </svg> ), QrCode: ({ className = "w-5 h-5" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M12 4v1m6 0h2m-6 0h-2v4h4V5zm-6 0H4v4h4V5zm0 10H4v4h4v-4zm10 0h-4v4h4v-4zm-4-6h.01M12 12h4v4h-4v-4zm-6 0h.01M6 12v.01M18 12v.01" /> </svg> ), Calendar: ({ className = "w-4 h-4" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <rect x="3" y="4" width="18" height="18" rx="2" ry="2" /> <line x1="16" y1="2" x2="16" y2="6" /> <line x1="8" y1="2" x2="8" y2="6" /> <line x1="3" y1="10" x2="21" y2="10" /> </svg> ), History: ({ className = "w-4 h-4" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <path strokeLinecap="round" strokeLinejoin="round" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg> ), Target: ({ className = "w-4 h-4" }) => ( <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}> <circle cx="12" cy="12" r="10" /> <circle cx="12" cy="12" r="6" /> <circle cx="12" cy="12" r="2" /> </svg> ) }; // --- SHOPEE MEMBERSHIP VIP CLUB MODAL --- function ShopeeMemberHubModal({ isOpen, onClose, currentUser, onLogout, userOrders, coinLogs, onProfileUpdate, onClaimCoins, onSpinWheel, onClaimMission, onCollectVoucher, onUploadSlipForOrder, onQuickSwitchTier, initialTab = 'tier' }) { const [activeTab, setActiveTab] = useState(initialTab); const [simulatedTier, setSimulatedTier] = useState(null); const [orderStatusFilter, setOrderStatusFilter] = useState('all'); const [isClaiming, setIsClaiming] = useState(false); const [isSpinning, setIsSpinning] = useState(false); const [spinResult, setSpinResult] = useState(null); const [wheelRotation, setWheelRotation] = useState(0); const [editForm, setEditForm] = useState({ fullName: currentUser?.full_name || '', phone: currentUser?.phone || '', address: currentUser?.address || '', gender: currentUser?.gender || 'other', birthday: currentUser?.birthday || '' }); const [avatarPreview, setAvatarPreview] = useState(currentUser?.avatar || ''); const [isSavingProfile, setIsSavingProfile] = useState(false); const [profileMsg, setProfileMsg] = useState(''); const [slipModalOrder, setSlipModalOrder] = useState(null); const [slipPreview, setSlipPreview] = useState(''); const [isUploadingSlip, setIsUploadingSlip] = useState(false); const fileInputRef = useRef(null); const slipInputRef = useRef(null); useEffect(() => { if (currentUser) { setEditForm({ fullName: currentUser.full_name || '', phone: currentUser.phone || '', address: currentUser.address || '', gender: currentUser.gender || 'other', birthday: currentUser.birthday || '' }); setAvatarPreview(currentUser.avatar || ''); } }, [currentUser]); useEffect(() => { if (initialTab) { setActiveTab(initialTab); } }, [initialTab, isOpen]); if (!isOpen || !currentUser) return null; const activeTierKey = simulatedTier || currentUser.membership_tier || 'classic'; const currentTier = SHOPEE_TIERS[activeTierKey] || SHOPEE_TIERS['classic']; const nextTierKey = currentTier.nextTier; const nextTier = nextTierKey ? SHOPEE_TIERS[nextTierKey] : null; let spentProgress = 100; let ordersProgress = 100; let remainingSpent = 0; let remainingOrders = 0; if (nextTier && currentTier.nextRequirement) { remainingSpent = Math.max(0, currentTier.nextRequirement.spent - currentUser.total_spent); remainingOrders = Math.max(0, currentTier.nextRequirement.orders - currentUser.orders_count); spentProgress = Math.min(100, Math.round((currentUser.total_spent / currentTier.nextRequirement.spent) * 100)); ordersProgress = Math.min(100, Math.round((currentUser.orders_count / currentTier.nextRequirement.orders) * 100)); } // Check-in status const todayStr = new Date().toISOString().slice(0, 10); const isAlreadyClaimedToday = currentUser.daily_checkin === todayStr; const streak = currentUser.checkin_streak || 0; const handleAvatarChange = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (ev) => setAvatarPreview(ev.target.result); reader.readAsDataURL(file); } }; const handleSaveProfile = async (e) => { e.preventDefault(); setIsSavingProfile(true); setProfileMsg(''); try { const payload = { action: 'update_profile', fullName: editForm.fullName, phone: editForm.phone, address: editForm.address, gender: editForm.gender, birthday: editForm.birthday, avatar_base64: avatarPreview && avatarPreview.startsWith('data:') ? avatarPreview : '' }; const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await res.json(); if (data.success) { setProfileMsg('บันทึกข้อมูลโปรไฟล์ Shopee เรียบร้อยแล้ว!'); onProfileUpdate(data.user); } else { setProfileMsg(data.message || 'บันทึกไม่สำเร็จ'); } } catch (err) { setProfileMsg('เกิดข้อผิดพลาดในการเชื่อมต่อเซิร์ฟเวอร์'); } finally { setIsSavingProfile(false); } }; const handleClaimCoinsClick = async () => { setIsClaiming(true); await onClaimCoins(); setIsClaiming(false); }; // Lucky Wheel Spin Logic const handleSpinWheelClick = async () => { if (isSpinning) return; setIsSpinning(true); setSpinResult(null); // Spin animation const extraTurns = 5 + Math.floor(Math.random() * 3); const randomDeg = Math.floor(Math.random() * 360); const totalDeg = wheelRotation + (extraTurns * 360) + randomDeg; setWheelRotation(totalDeg); try { const result = await onSpinWheel(); setTimeout(() => { setIsSpinning(false); if (result && result.prize) { setSpinResult(result.prize); } }, 3000); } catch (e) { setIsSpinning(false); } }; const handleSlipChange = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (ev) => setSlipPreview(ev.target.result); reader.readAsDataURL(file); } }; const handleUploadSlipSubmit = async (e) => { e.preventDefault(); if (!slipModalOrder || !slipPreview) return; setIsUploadingSlip(true); try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'upload_slip', orderId: slipModalOrder.orderId, slip_base64: slipPreview }) }); const data = await res.json(); if (data.success) { alert('อัปโหลดสลิปสำเร็จ! เจ้าหน้าที่จะตรวจสอบยอดชำระ'); setSlipModalOrder(null); setSlipPreview(''); onUploadSlipForOrder(slipModalOrder.orderId, data.slipUrl); } else { alert(data.message || 'อัปโหลดสลิปไม่สำเร็จ'); } } catch (err) { alert('เกิดข้อผิดพลาดในการอัปโหลดสลิป'); } finally { setIsUploadingSlip(false); } }; const filteredOrders = userOrders.filter(ord => { if (orderStatusFilter === 'all') return true; if (orderStatusFilter === 'pending') return ord.status.toLowerCase().includes('pending') || ord.status.toLowerCase().includes('slip'); if (orderStatusFilter === 'shipped') return ord.status.toLowerCase().includes('shipped') || ord.status.toLowerCase().includes('delivery'); if (orderStatusFilter === 'completed') return ord.status.toLowerCase().includes('completed') || ord.status.toLowerCase().includes('paid'); return true; }); return ( <div className="fixed inset-0 z-50 flex items-center justify-center p-2 sm:p-4 bg-slate-950/90 backdrop-blur-md animate-fade-in"> <div className="bg-[#0f172a] border border-slate-800 rounded-3xl max-w-4xl w-full p-4 sm:p-7 space-y-4 shadow-2xl max-h-[94vh] flex flex-col"> {/* TOP HEADER */} <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b border-slate-800 pb-3"> <div className="flex items-center space-x-3"> <div className="relative shrink-0"> {avatarPreview ? ( <img src={avatarPreview} alt="Avatar" className="w-13 h-13 rounded-2xl object-cover border-2 border-orange-500 shadow-md" /> ) : ( <div className="w-13 h-13 rounded-2xl shopee-gradient flex items-center justify-center font-black text-xl text-white shadow-lg"> {currentUser.username ? currentUser.username[0].toUpperCase() : 'U'} </div> )} <span className="absolute -bottom-1 -right-1 bg-amber-400 text-slate-950 font-black text-[9px] px-1.5 py-0.2 rounded-full shadow"> VIP </span> </div> <div> <div className="flex items-center space-x-2"> <h3 className="font-black text-base sm:text-lg text-white">{currentUser.full_name || currentUser.username}</h3> <span className={`px-2.5 py-0.5 rounded-full text-[10px] font-black uppercase text-white shadow bg-gradient-to-r ${currentTier.color}`}> {currentTier.name.split(' ')[0]} </span> </div> <p className="text-[11px] text-slate-400"> 🪙 Shopee Coins: <strong className="text-amber-400 font-extrabold text-xs">{currentUser.coins.toLocaleString()} Coins</strong> • ยอดซื้อสะสม ฿{currentUser.total_spent.toLocaleString()} </p> </div> </div> <div className="flex items-center space-x-2 self-end sm:self-auto"> <button onClick={onClose} className="p-2 rounded-xl bg-slate-800 text-slate-400 hover:text-white cursor-pointer" > <Icons.X className="w-5 h-5" /> </button> </div> </div> {/* TAB NAVIGATION */} <div className="flex overflow-x-auto space-x-1.5 pb-2 text-xs font-bold border-b border-slate-800 scrollbar-none"> <button onClick={() => setActiveTab('tier')} className={`px-3 py-2 rounded-xl transition-all whitespace-nowrap cursor-pointer flex items-center space-x-1.5 ${ activeTab === 'tier' ? 'bg-orange-500 text-white shadow-lg shadow-orange-500/20' : 'bg-slate-900 text-slate-400 hover:text-slate-200' }`} > <Icons.Sparkles className="w-3.5 h-3.5" /> <span>บัตร VIP & ระดับสมาชิก</span> </button> <button onClick={() => setActiveTab('streak')} className={`px-3 py-2 rounded-xl transition-all whitespace-nowrap cursor-pointer flex items-center space-x-1.5 ${ activeTab === 'streak' ? 'bg-amber-500 text-slate-950 font-black shadow-lg shadow-amber-500/20' : 'bg-slate-900 text-slate-400 hover:text-slate-200' }`} > <Icons.Calendar className="w-3.5 h-3.5" /> <span>เช็คอิน 7 วัน ({streak}/7)</span> </button> <button onClick={() => setActiveTab('spin')} className={`px-3 py-2 rounded-xl transition-all whitespace-nowrap cursor-pointer flex items-center space-x-1.5 ${ activeTab === 'spin' ? 'bg-violet-600 text-white shadow-lg' : 'bg-slate-900 text-slate-400 hover:text-slate-200' }`} > <span>🎡</span> <span>วงล้อลุ้นโชค</span> </button> <button onClick={() => setActiveTab('missions')} className={`px-3 py-2 rounded-xl transition-all whitespace-nowrap cursor-pointer flex items-center space-x-1.5 ${ activeTab === 'missions' ? 'bg-rose-600 text-white shadow-lg' : 'bg-slate-900 text-slate-400 hover:text-slate-200' }`} > <Icons.Target className="w-3.5 h-3.5" /> <span>ภารกิจสะสมเหรียญ</span> </button> <button onClick={() => setActiveTab('vouchers')} className={`px-3 py-2 rounded-xl transition-all whitespace-nowrap cursor-pointer flex items-center space-x-1.5 ${ activeTab === 'vouchers' ? 'bg-emerald-600 text-white shadow-lg' : 'bg-slate-900 text-slate-400 hover:text-slate-200' }`} > <Icons.Tag className="w-3.5 h-3.5" /> <span>กระเป๋าคูปอง ({currentUser.vouchers?.length || 0})</span> </button> <button onClick={() => setActiveTab('ledger')} className={`px-3 py-2 rounded-xl transition-all whitespace-nowrap cursor-pointer flex items-center space-x-1.5 ${ activeTab === 'ledger' ? 'bg-amber-600 text-white shadow-lg' : 'bg-slate-900 text-slate-400 hover:text-slate-200' }`} > <Icons.History className="w-3.5 h-3.5" /> <span>ประวัติเหรียญ</span> </button> <button onClick={() => setActiveTab('orders')} className={`px-3 py-2 rounded-xl transition-all whitespace-nowrap cursor-pointer flex items-center space-x-1.5 ${ activeTab === 'orders' ? 'bg-blue-600 text-white shadow-lg' : 'bg-slate-900 text-slate-400 hover:text-slate-200' }`} > <Icons.ShoppingBag className="w-3.5 h-3.5" /> <span>คำสั่งซื้อ ({userOrders.length})</span> </button> <button onClick={() => setActiveTab('profile')} className={`px-3 py-2 rounded-xl transition-all whitespace-nowrap cursor-pointer flex items-center space-x-1.5 ${ activeTab === 'profile' ? 'bg-indigo-600 text-white shadow-lg' : 'bg-slate-900 text-slate-400 hover:text-slate-200' }`} > <Icons.User className="w-3.5 h-3.5" /> <span>โปรไฟล์ & รูปถ่าย</span> </button> </div> {/* TAB 1: METALLIC VIP CARD & TIER PROGRESS */} {activeTab === 'tier' && ( <div className="space-y-4 overflow-y-auto pr-1"> {/* Holographic VIP Metallic Card */} <div className={`hologram-card rounded-3xl p-6 relative overflow-hidden bg-gradient-to-tr ${currentTier.color} text-white shadow-2xl border ${currentTier.border}`}> <div className="absolute top-0 right-0 p-6 opacity-15"> <Icons.Shopee className="w-36 h-36" /> </div> <div className="relative z-10 space-y-4"> <div className="flex justify-between items-start"> <div> <div className="flex items-center space-x-1.5"> <span className="text-[10px] uppercase tracking-widest font-black opacity-80">Shopee Member Club VIP Card</span> <span className="bg-white/20 px-2 py-0.5 rounded-full text-[9px] font-black">Official</span> </div> <h4 className="text-2xl sm:text-3xl font-black mt-0.5">{currentTier.name}</h4> </div> <span className="bg-black/30 backdrop-blur-md px-3 py-1 rounded-full text-xs font-black border border-white/20"> Tier Level {activeTierKey.toUpperCase()} </span> </div> <div className="grid grid-cols-2 sm:grid-cols-4 gap-2.5 pt-1"> <div className="bg-black/25 p-2.5 rounded-2xl backdrop-blur-sm"> <span className="text-[10px] opacity-75 block">ยอดช้อปสะสม</span> <span className="text-sm sm:text-base font-extrabold">฿{currentUser.total_spent.toLocaleString()}</span> </div> <div className="bg-black/25 p-2.5 rounded-2xl backdrop-blur-sm"> <span className="text-[10px] opacity-75 block">คำสั่งซื้อสำเร็จ</span> <span className="text-sm sm:text-base font-extrabold">{currentUser.orders_count} ออเดอร์</span> </div> <div className="bg-black/25 p-2.5 rounded-2xl backdrop-blur-sm"> <span className="text-[10px] opacity-75 block">Coins Cashback</span> <span className="text-sm sm:text-base font-extrabold text-amber-300">+{currentTier.coinCashback}%</span> </div> <div className="bg-black/25 p-2.5 rounded-2xl backdrop-blur-sm"> <span className="text-[10px] opacity-75 block">โค้ดส่งฟรีรายเดือน</span> <span className="text-sm sm:text-base font-extrabold text-emerald-300">x{currentTier.freeShippingVouchers} โค้ด</span> </div> </div> {/* Barcode & QR Code simulation */} <div className="flex items-center justify-between pt-2 border-t border-white/20 text-xs"> <div className="font-mono text-[11px] opacity-80"> ID: SHP-VIP-{currentUser.id || 8829}-{activeTierKey.toUpperCase()} </div> <div className="flex items-center space-x-2"> <span className="text-[10px] opacity-75">สแกนรับสิทธิ์:</span> <div className="bg-white p-1 rounded-lg"> <Icons.QrCode className="w-5 h-5 text-slate-900" /> </div> </div> </div> {/* Progress to Next Tier */} {nextTier ? ( <div className="bg-black/30 p-3.5 rounded-2xl space-y-2"> <div className="flex justify-between text-xs font-bold"> <span>เป้าหมายสู่ <strong>{nextTier.name}</strong></span> <span className="text-amber-300">ช้อปอีก ฿{remainingSpent.toLocaleString()} หรืออีก {remainingOrders} ออเดอร์</span> </div> <div className="w-full bg-white/20 rounded-full h-3 overflow-hidden"> <div className="bg-gradient-to-r from-amber-300 via-yellow-400 to-amber-500 h-full rounded-full transition-all duration-500" style={{ width: `${Math.max(spentProgress, ordersProgress)}%` }} ></div> </div> </div> ) : ( <div className="bg-black/30 p-3 rounded-2xl text-center text-xs font-black text-amber-300"> 👑 ยินดีด้วย! คุณอยู่ในระดับสูงสุด (Platinum Super VIP) ของ Shopee แล้ว </div> )} </div> </div> {/* 1-Click Demo Tier Simulator */} <div className="glass-card p-4 rounded-2xl border border-slate-800 space-y-2"> <div className="flex justify-between items-center text-xs"> <span className="font-extrabold text-slate-300">⚡ จำลองเปลี่ยนระดับสมาชิก (Demo Tier Simulator):</span> <span className="text-[10px] text-slate-500">คลิกเพื่อดูสิทธิประโยชน์แต่ละระดับ</span> </div> <div className="grid grid-cols-2 sm:grid-cols-4 gap-2 text-xs font-bold"> <button onClick={() => setSimulatedTier('classic')} className={`p-2 rounded-xl border text-center transition-all cursor-pointer ${ activeTierKey === 'classic' ? 'bg-slate-700 border-slate-500 text-white' : 'bg-slate-900 border-slate-800 text-slate-400' }`} > 🥉 Classic (0.-) </button> <button onClick={() => setSimulatedTier('silver')} className={`p-2 rounded-xl border text-center transition-all cursor-pointer ${ activeTierKey === 'silver' ? 'bg-zinc-600 border-zinc-400 text-white' : 'bg-slate-900 border-slate-800 text-slate-400' }`} > 🥈 Silver (1,000.-) </button> <button onClick={() => setSimulatedTier('gold')} className={`p-2 rounded-xl border text-center transition-all cursor-pointer ${ activeTierKey === 'gold' ? 'bg-amber-600 border-amber-400 text-white' : 'bg-slate-900 border-slate-800 text-slate-400' }`} > 🥇 Gold VIP (3,000.-) </button> <button onClick={() => setSimulatedTier('platinum')} className={`p-2 rounded-xl border text-center transition-all cursor-pointer ${ activeTierKey === 'platinum' ? 'bg-purple-600 border-purple-400 text-white' : 'bg-slate-900 border-slate-800 text-slate-400' }`} > 👑 Platinum (8,000.-) </button> </div> </div> {/* All Tier Comparison Cards */} <div className="space-y-2.5"> <h4 className="text-xs font-extrabold text-slate-300 uppercase tracking-wider"> สิทธิประโยชน์ประจำระดับสมาชิก Shopee Member Club </h4> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3 text-xs"> <div className="glass-card p-3.5 rounded-2xl border border-slate-800 flex items-start space-x-3"> <div className="p-2.5 rounded-xl bg-emerald-500/20 text-emerald-400 shrink-0"> <Icons.Truck className="w-5 h-5" /> </div> <div> <h5 className="font-bold text-white">โค้ดส่งฟรี x{currentTier.freeShippingVouchers} โค้ด/เดือน</h5> <p className="text-[11px] text-slate-400">รับโค้ดส่งฟรีทุกเดือนโดยไม่มีขั้นต่ำ</p> </div> </div> <div className="glass-card p-3.5 rounded-2xl border border-slate-800 flex items-start space-x-3"> <div className="p-2.5 rounded-xl bg-amber-500/20 text-amber-400 shrink-0"> <Icons.Coins className="w-5 h-5" /> </div> <div> <h5 className="font-bold text-white">เงินคืน {currentTier.coinCashback}% Shopee Coins</h5> <p className="text-[11px] text-slate-400">รับเหรียญคืนทุกการสั่งซื้อสินค้าในร้าน</p> </div> </div> <div className="glass-card p-3.5 rounded-2xl border border-slate-800 flex items-start space-x-3"> <div className="p-2.5 rounded-xl bg-orange-500/20 text-orange-400 shrink-0"> <Icons.Tag className="w-5 h-5" /> </div> <div> <h5 className="font-bold text-white">ส่วนลดพิเศษ {currentTier.discountPercent}%</h5> <p className="text-[11px] text-slate-400">สิทธิรับส่วนลดในแคมเปญวันพิเศษและวันเกิด</p> </div> </div> <div className="glass-card p-3.5 rounded-2xl border border-slate-800 flex items-start space-x-3"> <div className="p-2.5 rounded-xl bg-purple-500/20 text-purple-400 shrink-0"> <Icons.Sparkles className="w-5 h-5" /> </div> <div> <h5 className="font-bold text-white">Shopee Priority Support</h5> <p className="text-[11px] text-slate-400">บริการให้คำปรึกษาและจัดส่งสินค้าลำดับแรก</p> </div> </div> </div> </div> </div> )} {/* TAB 2: 7-DAY STREAK DAILY CHECK-IN */} {activeTab === 'streak' && ( <div className="space-y-4 overflow-y-auto pr-1"> <div className="glass-panel p-5 rounded-3xl border border-amber-500/30 bg-gradient-to-br from-amber-950/30 to-slate-900 space-y-4"> <div className="flex flex-col sm:flex-row items-center justify-between gap-4"> <div className="flex items-center space-x-4"> <div className="w-16 h-16 rounded-3xl bg-gradient-to-tr from-amber-400 to-yellow-500 text-slate-950 flex items-center justify-center font-black text-2xl shadow-xl shadow-amber-500/20"> 🪙 </div> <div> <span className="text-xs text-amber-300 font-bold uppercase tracking-wider">Shopee Coins Balance</span> <h4 className="text-3xl font-black text-white">{currentUser.coins.toLocaleString()} <span className="text-base font-bold text-amber-400">Coins</span></h4> <p className="text-[11px] text-slate-400">สะสมเช็คอินครบ 7 วันรับโบนัส Super Box สูงสุด +50 Coins!</p> </div> </div> <button onClick={handleClaimCoinsClick} disabled={isAlreadyClaimedToday || isClaiming} className={`px-6 py-3.5 rounded-2xl font-black text-xs transition-all shadow-xl flex items-center space-x-2 cursor-pointer ${ isAlreadyClaimedToday ? 'bg-slate-800 text-slate-500 cursor-not-allowed border border-slate-700' : 'bg-gradient-to-r from-amber-400 to-yellow-500 text-slate-950 hover:brightness-110 shadow-amber-500/30 scale-105' }`} > <Icons.Coins className="w-4 h-4" /> <span>{isAlreadyClaimedToday ? '✓ เช็คอินวันนี้แล้ว' : (isClaiming ? 'กำลังเช็คอิน...' : `กดรับฟรีวันที่ ${streak + 1 || 1}`)}</span> </button> </div> </div> {/* 7-Day Streak Cards Calendar */} <div className="space-y-2.5"> <div className="flex justify-between items-center text-xs"> <h4 className="font-extrabold text-slate-300 uppercase tracking-wider">ปฏิทินเช็คอินรับเหรียญ 7 วัน (7-Day Streak)</h4> <span className="text-amber-400 font-bold">สะสมต่อเนื่อง: {streak} / 7 วัน</span> </div> <div className="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-7 gap-2"> {[1, 2, 3, 4, 5, 6, 7].map((dayNum) => { const reward = STREAK_REWARDS[dayNum] || { coins: 10, label: '+10' }; const isCompleted = streak >= dayNum; const isCurrent = streak + 1 === dayNum; return ( <div key={dayNum} className={`p-3 rounded-2xl border text-center flex flex-col justify-between space-y-2 relative overflow-hidden transition-all ${ isCompleted ? 'bg-emerald-950/40 border-emerald-500/50 text-emerald-300' : isCurrent && !isAlreadyClaimedToday ? 'bg-amber-950/40 border-amber-400 text-white shadow-lg shadow-amber-500/20 scale-105' : 'bg-slate-900 border-slate-800 text-slate-400' }`} > {isCompleted && ( <span className="absolute top-1.5 right-1.5 text-[9px] bg-emerald-500 text-slate-950 font-black px-1 rounded-full"> ✓ </span> )} <span className="text-[10px] font-bold block">Day {dayNum}</span> <span className="text-2xl">{dayNum === 7 ? '🎁' : '🪙'}</span> <span className="text-xs font-black text-amber-400">{reward.label.split(' ')[0]}</span> <span className="text-[9px] opacity-75">{dayNum === 7 ? 'Super Box' : 'Coins'}</span> </div> ); })} </div> </div> </div> )} {/* TAB 3: LUCKY SPIN WHEEL */} {activeTab === 'spin' && ( <div className="space-y-4 overflow-y-auto pr-1 text-center"> <div className="max-w-md mx-auto space-y-4"> <div> <h4 className="text-lg font-black text-white">🎡 Shopee Lucky Rewards Wheel</h4> <p className="text-xs text-slate-400">หมุนวงล้อเสี่ยงโชคลุ้นรับ Shopee Coins & โค้ดส่งฟรีทุกวัน!</p> </div> {/* ROTATING WHEEL SIMULATION */} <div className="relative w-64 h-64 mx-auto my-2"> {/* Wheel Pointer Needle */} <div className="absolute -top-3 left-1/2 -translate-x-1/2 z-20 w-0 h-0 border-l-[10px] border-l-transparent border-r-[10px] border-r-transparent border-t-[18px] border-t-amber-400 drop-shadow-md"></div> {/* Wheel Body */} <div className="w-full h-full rounded-full border-4 border-amber-400 shadow-2xl transition-transform duration-[3000ms] cubic-bezier(0.2, 0.8, 0.2, 1) relative overflow-hidden" style={{ transform: `rotate(${wheelRotation}deg)`, background: 'conic-gradient(#f59e0b 0deg 60deg, #10b981 60deg 120deg, #ea580c 120deg 180deg, #8b5cf6 180deg 240deg, #e11d48 240deg 300deg, #3b82f6 300deg 360deg)' }} > <div className="absolute inset-0 flex items-center justify-center"> <div className="w-16 h-16 rounded-full bg-slate-950 border-2 border-amber-400 flex items-center justify-center font-black text-amber-400 text-xs shadow-inner"> SPIN </div> </div> </div> </div> {spinResult && ( <div className="bg-amber-950/60 border border-amber-500 text-amber-200 p-4 rounded-2xl animate-fade-in font-bold text-xs space-y-1"> <span className="text-2xl block">{spinResult.icon || '🎉'}</span> <p className="text-sm font-black text-white">ยินดีด้วย! คุณได้รับ {spinResult.name}</p> <p className="text-[11px] text-amber-400">เหรียญหรือคูปองถูกเพิ่มเข้าสู่บัญชีของคุณเรียบร้อยแล้ว</p> </div> )} <button onClick={handleSpinWheelClick} disabled={isSpinning} className="w-full py-3.5 rounded-2xl shopee-gradient hover:brightness-110 text-white font-black text-xs shadow-xl cursor-pointer disabled:opacity-50" > {isSpinning ? 'กำลังหมุนวงล้อ...' : '🎰 หมุนวงล้อเสี่ยงโชค (SPIN NOW)'} </button> </div> </div> )} {/* TAB 4: SHOPEE MISSIONS */} {activeTab === 'missions' && ( <div className="space-y-4 overflow-y-auto pr-1"> <div className="flex justify-between items-center"> <div> <h4 className="text-xs font-extrabold text-slate-300 uppercase tracking-wider">ภารกิจ Shopee Missions สะสมเหรียญ</h4> <p className="text-[11px] text-slate-400">ทำภารกิจสำเร็จแล้วกดรับ Coins ได้ทันที</p> </div> <span className="text-xs text-amber-400 font-bold">รับ Coins ฟรี</span> </div> <div className="space-y-2.5"> {SHOPEE_MISSIONS.map((m) => { const isCompleted = currentUser.missions_completed?.includes(m.id); return ( <div key={m.id} className="glass-card p-4 rounded-2xl border border-slate-800 flex items-center justify-between gap-3 text-xs" > <div className="flex items-center space-x-3"> <div className="w-10 h-10 rounded-xl bg-orange-500/20 text-orange-400 flex items-center justify-center font-bold text-lg shrink-0"> {m.category === 'profile' ? '👤' : (m.category === 'checkin' ? '📅' : (m.category === 'game' ? '🎡' : (m.category === 'shopping' ? '🛍️' : '🎟️')))} </div> <div> <h5 className="font-bold text-white text-xs">{m.title}</h5> <p className="text-[11px] text-slate-400">{m.subtitle}</p> </div> </div> <div className="flex items-center space-x-3 shrink-0"> <span className="text-xs font-extrabold text-amber-400">+{m.rewardCoins} Coins</span> <button onClick={() => onClaimMission(m.id)} disabled={isCompleted} className={`px-3.5 py-1.5 rounded-xl font-black text-xs transition-all cursor-pointer ${ isCompleted ? 'bg-slate-800 text-emerald-400 border border-emerald-500/30' : 'shopee-gradient text-white hover:brightness-110 shadow-md' }`} > {isCompleted ? '✓ รับแล้ว' : 'กดรับรางวัล'} </button> </div> </div> ); })} </div> </div> )} {/* TAB 5: VOUCHER WALLET */} {activeTab === 'vouchers' && ( <div className="space-y-4 overflow-y-auto pr-1"> <div className="flex justify-between items-center"> <h4 className="text-xs font-extrabold text-slate-300 uppercase tracking-wider">กระเป๋าคูปองส่วนลดและโค้ดส่งฟรี Shopee</h4> <span className="text-xs text-emerald-400 font-bold">เก็บได้ทุกคน</span> </div> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> {AVAILABLE_VOUCHERS.map((v) => { const isCollected = currentUser.vouchers?.includes(v.code); return ( <div key={v.code} className="glass-card rounded-2xl border border-slate-800 p-4 flex flex-col justify-between space-y-3 relative overflow-hidden"> <div className="flex items-start justify-between"> <div className="flex items-center space-x-2"> <span className={`px-2.5 py-0.5 rounded-md text-[10px] font-black text-white ${v.color}`}> {v.tag} </span> <span className="text-xs font-bold text-white">{v.title}</span> </div> </div> <p className="text-[11px] text-slate-400">{v.subtitle}</p> <div className="flex items-center justify-between pt-2 border-t border-slate-800/80"> <span className="font-mono text-xs font-extrabold text-orange-400">{v.code}</span> <button onClick={() => onCollectVoucher(v.code)} disabled={isCollected} className={`px-4 py-1.5 rounded-xl text-xs font-black transition-all cursor-pointer ${ isCollected ? 'bg-slate-800 text-emerald-400 border border-emerald-500/30' : 'shopee-gradient text-white hover:brightness-110 shadow-md' }`} > {isCollected ? '✓ เก็บแล้ว' : 'เก็บโค้ด'} </button> </div> </div> ); })} </div> </div> )} {/* TAB 6: COINS LEDGER & TRANSACTION HISTORY */} {activeTab === 'ledger' && ( <div className="space-y-4 overflow-y-auto pr-1"> <div className="flex justify-between items-center"> <h4 className="text-xs font-extrabold text-slate-300 uppercase tracking-wider">สมุดประวัติการรับและการใช้ Shopee Coins</h4> <span className="text-xs text-amber-400 font-bold">คงเหลือ: {currentUser.coins.toLocaleString()} Coins</span> </div> {coinLogs && coinLogs.length > 0 ? ( <div className="space-y-2 text-xs"> {coinLogs.map((log) => ( <div key={log.id} className="glass-card p-3 rounded-2xl border border-slate-800 flex items-center justify-between"> <div> <p className="font-bold text-white">{log.description}</p> <span className="text-[10px] text-slate-500">{log.created_at}</span> </div> <div className="text-right"> <span className={`font-black text-sm ${log.amount > 0 ? 'text-emerald-400' : 'text-rose-400'}`}> {log.amount > 0 ? `+${log.amount}` : log.amount} Coins </span> <span className="block text-[10px] text-slate-500">คงเหลือ: {log.balance_after}</span> </div> </div> ))} </div> ) : ( <div className="py-12 text-center text-slate-400 text-xs bg-slate-900/60 rounded-3xl border border-slate-800"> <p className="font-bold">ยังไม่มีประวัติธุรกรรมเหรียญ</p> <p className="text-[10px] text-slate-500 mt-1">กดเช็คอินรายวันหรือหมุนวงล้อเพื่อเริ่มรับเหรียญ</p> </div> )} </div> )} {/* TAB 7: MY PURCHASES */} {activeTab === 'orders' && ( <div className="space-y-4 overflow-y-auto pr-1"> <div className="grid grid-cols-4 gap-1.5 bg-slate-900 p-1 rounded-2xl text-[11px] font-bold text-center"> <button onClick={() => setOrderStatusFilter('all')} className={`py-2 rounded-xl transition-all cursor-pointer ${orderStatusFilter === 'all' ? 'bg-orange-500 text-white shadow' : 'text-slate-400 hover:text-white'}`}>ทั้งหมด</button> <button onClick={() => setOrderStatusFilter('pending')} className={`py-2 rounded-xl transition-all cursor-pointer ${orderStatusFilter === 'pending' ? 'bg-orange-500 text-white shadow' : 'text-slate-400 hover:text-white'}`}>ที่ต้องชำระ/สลิป</button> <button onClick={() => setOrderStatusFilter('shipped')} className={`py-2 rounded-xl transition-all cursor-pointer ${orderStatusFilter === 'shipped' ? 'bg-orange-500 text-white shadow' : 'text-slate-400 hover:text-white'}`}>ที่ต้องจัดส่ง</button> <button onClick={() => setOrderStatusFilter('completed')} className={`py-2 rounded-xl transition-all cursor-pointer ${orderStatusFilter === 'completed' ? 'bg-orange-500 text-white shadow' : 'text-slate-400 hover:text-white'}`}>สำเร็จแล้ว</button> </div> {filteredOrders.length === 0 ? ( <div className="py-16 text-center text-slate-400 space-y-2 bg-slate-900/60 rounded-3xl border border-slate-800"> <p className="font-bold text-sm">ไม่มีรายการคำสั่งซื้อในหมวดนี้</p> <p className="text-xs text-slate-500">เลือกช้อปสินค้าแฟชั่นเพื่อรับ Coins คืนได้ทันที</p> </div> ) : ( <div className="space-y-3"> {filteredOrders.map((ord) => ( <div key={ord.orderId} className="glass-card rounded-2xl border border-slate-800 p-4 space-y-3"> <div className="flex justify-between items-center border-b border-slate-800 pb-2"> <div className="flex items-center space-x-2"> <span className="px-2 py-0.5 rounded bg-rose-600 text-white text-[10px] font-black">Shopee Mall</span> <span className="font-mono font-extrabold text-xs text-orange-400">{ord.orderId}</span> </div> <span className="px-2.5 py-0.5 rounded-full text-[10px] font-black bg-orange-500/20 text-orange-300 border border-orange-500/30"> {ord.status} </span> </div> <div className="space-y-2"> {ord.items && ord.items.map((item, idx) => ( <div key={idx} className="flex items-center justify-between text-xs"> <div className="flex items-center space-x-2.5"> <div className="w-9 h-9 rounded-lg bg-slate-800 border border-slate-700 flex items-center justify-center font-bold text-[10px] text-white shrink-0"> {item.size?.label || 'M'} </div> <div> <p className="font-bold text-slate-200">{item.title || 'Apex Heavyweight Tee'}</p> <p className="text-[10px] text-slate-400">สี: {item.color?.name || 'Classic'} • จำนวน: x{item.quantity}</p> </div> </div> <span className="font-bold text-slate-200">฿{(item.price * item.quantity).toLocaleString()}</span> </div> ))} </div> <div className="pt-2 border-t border-slate-800 flex flex-col sm:flex-row items-start sm:items-center justify-between gap-2 text-xs"> <div className="text-[11px] text-slate-400"> <span>สั่งเมื่อ: {ord.createdAt} • </span> <span className="text-emerald-400 font-semibold">รับ +{ord.coinsEarned || 20} Coins</span> </div> <div className="flex items-center space-x-3 w-full sm:w-auto justify-between sm:justify-end"> <div> <span className="text-[10px] text-slate-400">ยอดชำระ: </span> <span className="text-sm font-black text-white">฿{ord.grandTotal.toLocaleString()}</span> </div> {(!ord.slipImage || ord.status === 'Pending Payment') && ( <button onClick={() => { setSlipModalOrder(ord); setSlipPreview(ord.slipImage || ''); }} className="px-3 py-1.5 rounded-xl shopee-gradient text-white text-[11px] font-extrabold cursor-pointer shadow" > แจ้งโอน / แนบสลิป </button> )} {ord.slipImage && ( <a href={ord.slipImage} target="_blank" rel="noreferrer" className="px-2.5 py-1.5 rounded-xl bg-slate-800 text-emerald-400 border border-emerald-500/30 text-[10px] font-bold" > ดูสลิป </a> )} </div> </div> </div> ))} </div> )} {/* SLIP UPLOAD MODAL */} {slipModalOrder && ( <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/90 backdrop-blur-md"> <div className="bg-slate-900 border border-slate-800 rounded-3xl max-w-md w-full p-6 space-y-4 shadow-2xl"> <div className="flex justify-between items-center border-b border-slate-800 pb-3"> <h4 className="font-extrabold text-sm text-white">แนบสลิปโอนเงิน (ออเดอร์: {slipModalOrder.orderId})</h4> <button onClick={() => setSlipModalOrder(null)} className="text-slate-400 hover:text-white cursor-pointer"><Icons.X className="w-5 h-5" /></button> </div> <form onSubmit={handleUploadSlipSubmit} className="space-y-4"> <div onClick={() => slipInputRef.current?.click()} className="border-2 border-dashed border-slate-700 hover:border-orange-500 rounded-2xl p-6 text-center cursor-pointer bg-slate-950/60 transition-all" > <input type="file" ref={slipInputRef} onChange={handleSlipChange} accept="image/*" className="hidden" /> {slipPreview ? ( <img src={slipPreview} alt="Slip Preview" className="max-h-56 mx-auto rounded-xl object-contain shadow-lg" /> ) : ( <div className="space-y-2"> <Icons.Upload className="w-8 h-8 text-orange-400 mx-auto" /> <p className="text-xs font-bold text-slate-200">คลิกเพื่อเลือกไฟล์รูปภาพสลิปจากเครื่อง</p> <p className="text-[10px] text-slate-500">รองรับ JPG, PNG, WEBP</p> </div> )} </div> <button type="submit" disabled={!slipPreview || isUploadingSlip} className="w-full py-3 rounded-xl shopee-gradient text-white font-black text-xs cursor-pointer shadow-lg disabled:opacity-40" > {isUploadingSlip ? 'กำลังอัปโหลด...' : 'ยืนยันการส่งสลิปโอนเงิน'} </button> </form> </div> </div> )} </div> )} {/* TAB 8: PROFILE & SETTINGS */} {activeTab === 'profile' && ( <form onSubmit={handleSaveProfile} className="space-y-4 overflow-y-auto pr-1 text-xs"> {profileMsg && ( <div className="bg-emerald-950/80 border border-emerald-500/50 text-emerald-200 p-3 rounded-xl font-bold"> {profileMsg} </div> )} <div className="flex items-center space-x-4 bg-slate-900 p-4 rounded-2xl border border-slate-800"> <div className="relative"> {avatarPreview ? ( <img src={avatarPreview} alt="Profile" className="w-16 h-16 rounded-2xl object-cover border-2 border-orange-500 shadow-md" /> ) : ( <div className="w-16 h-16 rounded-2xl shopee-gradient flex items-center justify-center font-black text-2xl text-white"> {currentUser.username ? currentUser.username[0].toUpperCase() : 'U'} </div> )} </div> <div className="space-y-1.5 flex-1"> <h5 className="font-bold text-slate-200 text-xs">รูปภาพโปรไฟล์สมาชิก (Avatar Photo)</h5> <p className="text-[10px] text-slate-400">เลือกไฟล์รูปภาพจริงจากเครื่องคอมพิวเตอร์ของคุณ</p> <input type="file" ref={fileInputRef} onChange={handleAvatarChange} accept="image/*" className="hidden" /> <button type="button" onClick={() => fileInputRef.current?.click()} className="px-3.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 border border-slate-700 text-orange-400 font-bold text-xs flex items-center space-x-1.5 cursor-pointer" > <Icons.Upload className="w-3.5 h-3.5" /> <span>เลือกไฟล์รูปภาพจากเครื่อง...</span> </button> </div> </div> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> <div> <label className="block text-slate-300 font-semibold mb-1">ชื่อ-นามสกุล</label> <input type="text" required value={editForm.fullName} onChange={(e) => setEditForm({ ...editForm, fullName: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> <div> <label className="block text-slate-300 font-semibold mb-1">เบอร์โทรศัพท์ (10 หลัก)</label> <input type="tel" required value={editForm.phone} onChange={(e) => setEditForm({ ...editForm, phone: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> </div> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> <div> <label className="block text-slate-300 font-semibold mb-1">เพศ (Gender)</label> <select value={editForm.gender} onChange={(e) => setEditForm({ ...editForm, gender: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" > <option value="male">ชาย (Male)</option> <option value="female">หญิง (Female)</option> <option value="other">อื่นๆ (Other)</option> </select> </div> <div> <label className="block text-slate-300 font-semibold mb-1">วันเกิด (รับคูปองวันเกิด VIP)</label> <input type="date" value={editForm.birthday} onChange={(e) => setEditForm({ ...editForm, birthday: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> </div> <div> <label className="block text-slate-300 font-semibold mb-1">ที่อยู่จัดส่งสินค้าหลัก</label> <textarea rows={2} value={editForm.address} onChange={(e) => setEditForm({ ...editForm, address: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" ></textarea> </div> <div className="flex justify-between items-center pt-2 border-t border-slate-800"> <button type="button" onClick={onLogout} className="px-4 py-2 rounded-xl bg-rose-950 text-rose-300 border border-rose-500/30 font-bold hover:bg-rose-900 cursor-pointer" > ออกจากระบบ (Logout) </button> <button type="submit" disabled={isSavingProfile} className="px-6 py-2.5 rounded-xl shopee-gradient text-white font-black hover:brightness-110 shadow-lg cursor-pointer disabled:opacity-40" > {isSavingProfile ? 'กำลังบันทึก...' : 'บันทึกข้อมูลส่วนตัว'} </button> </div> </form> )} </div> </div> ); } // --- QUICK DEMO SWITCHER BAR COMPONENT --- function QuickDemoSwitcher({ onQuickSwitch }) { return ( <div className="bg-slate-900/90 border border-orange-500/30 p-3 rounded-2xl space-y-2"> <div className="flex items-center justify-between text-xs"> <span className="font-extrabold text-orange-400 flex items-center space-x-1.5"> <span>⚡</span> <span>1-Click Quick Demo Accounts (สลับบัญชีทดสอบ):</span> </span> <span className="text-[10px] text-slate-400">คลิกเข้าใช้งานได้ทันที</span> </div> <div className="grid grid-cols-2 sm:grid-cols-5 gap-1.5 text-xs font-bold"> <button type="button" onClick={() => onQuickSwitch('platinum')} className="px-2.5 py-1.5 rounded-xl bg-purple-950/60 hover:bg-purple-900 border border-purple-500/40 text-purple-300 cursor-pointer text-center" > 👑 Platinum VIP </button> <button type="button" onClick={() => onQuickSwitch('gold')} className="px-2.5 py-1.5 rounded-xl bg-amber-950/60 hover:bg-amber-900 border border-amber-500/40 text-amber-300 cursor-pointer text-center" > 🥇 Gold VIP </button> <button type="button" onClick={() => onQuickSwitch('silver')} className="px-2.5 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 border border-slate-500/40 text-slate-200 cursor-pointer text-center" > 🥈 Silver Member </button> <button type="button" onClick={() => onQuickSwitch('classic')} className="px-2.5 py-1.5 rounded-xl bg-slate-900 hover:bg-slate-800 border border-slate-700 text-slate-300 cursor-pointer text-center" > 🥉 Classic (ใหม่) </button> <button type="button" onClick={() => onQuickSwitch('admin')} className="px-2.5 py-1.5 rounded-xl bg-rose-950/60 hover:bg-rose-900 border border-rose-500/40 text-rose-300 cursor-pointer text-center col-span-2 sm:col-span-1" > 🛠️ Admin </button> </div> </div> ); } // --- ADD CUSTOM PRODUCT MODAL --- function AddProductModal({ isOpen, onClose, onProductAdded }) { const [formData, setFormData] = useState({ name: '', category: 't-shirts', category_name: 'เสื้อยืด (T-Shirts)', subtitle: '', price: 390, original_price: 690, description: '', badge: 'NEW ARRIVAL', badge_color: 'bg-emerald-600', sizes: 'S, M, L, XL, 2XL' }); const [imagePreview, setImagePreview] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const [error, setError] = useState(''); const productImgInputRef = useRef(null); if (!isOpen) return null; const handleImageSelect = (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (ev) => setImagePreview(ev.target.result); reader.readAsDataURL(file); } }; const handleSubmit = async (e) => { e.preventDefault(); setError(''); if (!imagePreview) { setError('กรุณาเลือกไฟล์รูปภาพสินค้าจากเครื่อง'); return; } setIsSubmitting(true); try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'add_product', name: formData.name, category: formData.category, category_name: formData.category === 't-shirts' ? 'เสื้อยืด (T-Shirts)' : (formData.category === 'hoodies' ? 'เสื้อกันหนาว & แจ็คเก็ต' : (formData.category === 'pants' ? 'กางเกง (Pants)' : 'แอคเซสเซอรี่')), subtitle: formData.subtitle, price: formData.price, original_price: formData.original_price, description: formData.description, badge: formData.badge, badge_color: formData.badge_color, sizes: formData.sizes, image_base64: imagePreview }) }); const data = await res.json(); if (data.success) { alert('เพิ่มสินค้าใหม่พร้อมรูปภาพเข้าสู่ระบบสำเร็จ!'); onProductAdded(data.allProducts); onClose(); } else { setError(data.message || 'เกิดข้อผิดพลาดในการเพิ่มสินค้า'); } } catch (err) { setError('เกิดข้อผิดพลาดในการเชื่อมต่อเซิร์ฟเวอร์'); } finally { setIsSubmitting(false); } }; return ( <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/85 backdrop-blur-md animate-fade-in"> <div className="bg-slate-900 border border-slate-800 rounded-3xl max-w-xl w-full p-6 sm:p-8 space-y-5 shadow-2xl max-h-[90vh] overflow-y-auto"> <div className="flex items-center justify-between border-b border-slate-800 pb-3"> <div className="flex items-center space-x-2"> <div className="w-9 h-9 rounded-xl bg-orange-500/20 text-orange-400 flex items-center justify-center font-bold"> <Icons.Plus className="w-5 h-5" /> </div> <div> <h3 className="font-extrabold text-base text-white">เพิ่มสินค้าใหม่ & อัปโหลดรูปภาพ (Add Product)</h3> <p className="text-[11px] text-slate-400">อัปโหลดไฟล์รูปภาพสินค้าจริงเพื่อจำหน่ายในร้าน APEX STUDIO</p> </div> </div> <button onClick={onClose} className="p-2 rounded-xl bg-slate-800 text-slate-400 hover:text-white cursor-pointer"> <Icons.X className="w-5 h-5" /> </button> </div> {error && ( <div className="bg-rose-950/80 border border-rose-500/50 text-rose-200 text-xs p-3 rounded-xl font-bold"> {error} </div> )} <form onSubmit={handleSubmit} className="space-y-4 text-xs"> <div> <label className="block font-bold text-slate-200 mb-1.5"> รูปภาพสินค้าจริง (Product Photo File) <span className="text-orange-400">*</span> </label> <div onClick={() => productImgInputRef.current?.click()} className="border-2 border-dashed border-slate-700 hover:border-orange-500 rounded-2xl p-4 text-center cursor-pointer bg-slate-950/60 transition-all" > <input type="file" ref={productImgInputRef} onChange={handleImageSelect} accept="image/*" className="hidden" /> {imagePreview ? ( <div className="relative aspect-video max-h-48 mx-auto overflow-hidden rounded-xl"> <img src={imagePreview} alt="Preview" className="w-full h-full object-cover rounded-xl" /> <span className="absolute bottom-2 right-2 bg-slate-950/80 px-2 py-1 rounded text-[10px] text-orange-400 font-bold"> คลิกเพื่อเปลี่ยนรูป </span> </div> ) : ( <div className="py-4 space-y-1.5"> <Icons.Image className="w-8 h-8 text-orange-400 mx-auto" /> <p className="text-xs font-bold text-slate-200">คลิกเพื่อเลือกไฟล์รูปภาพสินค้าจากเครื่อง (JPG, PNG, WEBP)</p> <p className="text-[10px] text-slate-500">แนะนำรูปทรงจัตุรัสหรือสัดส่วน 4:5 คมชัดแบบ Shopee Mall</p> </div> )} </div> </div> <div> <label className="block font-semibold text-slate-300 mb-1">ชื่อสินค้า (Product Name) <span className="text-orange-400">*</span></label> <input type="text" required placeholder="เช่น Apex Heavyweight Oversized Tee ลายใหม่ 2026" value={formData.name} onChange={(e) => setFormData({ ...formData, name: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> <div className="grid grid-cols-2 gap-3"> <div> <label className="block font-semibold text-slate-300 mb-1">หมวดหมู่</label> <select value={formData.category} onChange={(e) => setFormData({ ...formData, category: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" > <option value="t-shirts">เสื้อยืด (T-Shirts)</option> <option value="hoodies">เสื้อกันหนาว & แจ็คเก็ต</option> <option value="pants">กางเกง (Pants)</option> <option value="accessories">แอคเซสเซอรี่ (Accessories)</option> </select> </div> <div> <label className="block font-semibold text-slate-300 mb-1">ป้ายกำกับ (Badge)</label> <select value={formData.badge} onChange={(e) => setFormData({ ...formData, badge: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" > <option value="SHOPEE MALL">SHOPEE MALL (ป้ายมอลล์)</option> <option value="BEST SELLER">BEST SELLER (ขายดี)</option> <option value="NEW ARRIVAL">NEW ARRIVAL (มาใหม่)</option> <option value="FLASH SALE">FLASH SALE (ลดฟ้าผ่า)</option> </select> </div> </div> <div className="grid grid-cols-2 gap-3"> <div> <label className="block font-semibold text-slate-300 mb-1">ราคาขาย (THB) <span className="text-orange-400">*</span></label> <input type="number" required min="1" value={formData.price} onChange={(e) => setFormData({ ...formData, price: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> <div> <label className="block font-semibold text-slate-300 mb-1">ราคาเต็ม (ก่อนลด)</label> <input type="number" value={formData.original_price} onChange={(e) => setFormData({ ...formData, original_price: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> </div> <div> <label className="block font-semibold text-slate-300 mb-1">คำบรรยายสินค้า</label> <textarea rows={2} placeholder="รายละเอียดเนื้อผ้า ขนาด ทรงเสื้อ การดูแลรักษา..." value={formData.description} onChange={(e) => setFormData({ ...formData, description: e.target.value })} className="w-full py-2.5 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" ></textarea> </div> <button type="submit" disabled={isSubmitting} className="w-full py-3.5 rounded-xl shopee-gradient text-white font-black text-xs shadow-lg cursor-pointer disabled:opacity-40" > {isSubmitting ? 'กำลังบันทึกสินค้า...' : 'บันทึกและวางขายทันที'} </button> </form> </div> </div> ); } // --- PRODUCT DETAIL MODAL --- function ProductDetailModal({ isOpen, onClose, product, onAddToCart }) { const [selectedColor, setSelectedColor] = useState(null); const [selectedSize, setSelectedSize] = useState('M'); const [qty, setQty] = useState(1); const [activeGalleryImg, setActiveGalleryImg] = useState(''); useEffect(() => { if (product) { if (product.colors && product.colors.length > 0) { setSelectedColor(product.colors[0]); } if (product.sizes && product.sizes.length > 0) { setSelectedSize(product.sizes[0]); } setActiveGalleryImg(product.image); setQty(1); } }, [product]); if (!isOpen || !product) return null; const gallery = product.gallery && product.gallery.length > 0 ? product.gallery : [product.image]; return ( <div className="fixed inset-0 z-50 flex items-center justify-center p-3 sm:p-4 bg-slate-950/85 backdrop-blur-md animate-fade-in"> <div className="bg-[#111827] border border-slate-800 rounded-3xl max-w-3xl w-full p-5 sm:p-7 space-y-5 shadow-2xl max-h-[92vh] overflow-y-auto"> <div className="flex justify-between items-start"> <div className="flex items-center space-x-2"> <span className="px-2.5 py-0.5 rounded-full text-[10px] font-black text-white bg-rose-600 shadow"> SHOPEE MALL </span> <span className="text-xs text-slate-400 font-semibold">100% Authentic Guarantee</span> </div> <button onClick={onClose} className="p-2 rounded-xl bg-slate-800 text-slate-400 hover:text-white cursor-pointer"> <Icons.X className="w-5 h-5" /> </button> </div> <div className="grid grid-cols-1 md:grid-cols-2 gap-6 items-start"> <div className="space-y-3"> <div className="relative rounded-2xl overflow-hidden aspect-[4/5] border border-slate-800 shadow-xl bg-slate-950"> <img src={activeGalleryImg || product.image} alt={product.name} className="w-full h-full object-cover transition-all duration-300" /> <span className="absolute bottom-3 left-3 bg-slate-950/80 backdrop-blur-md px-2.5 py-1 rounded-full text-[10px] text-amber-400 font-bold border border-slate-700 flex items-center space-x-1"> <span>★ {product.rating || 4.9}</span> <span className="text-slate-400">({product.reviews_count || 142} รีวิว)</span> </span> </div> {gallery.length > 1 && ( <div className="flex space-x-2 overflow-x-auto pb-1"> {gallery.map((img, idx) => ( <button key={idx} onClick={() => setActiveGalleryImg(img)} className={`w-14 h-14 rounded-xl overflow-hidden border-2 shrink-0 transition-all cursor-pointer ${ activeGalleryImg === img ? 'border-orange-500 scale-105 shadow-md' : 'border-slate-800 opacity-60' }`} > <img src={img} alt="Thumb" className="w-full h-full object-cover" /> </button> ))} </div> )} </div> <div className="space-y-4 text-xs"> <div> <span className="text-[10px] text-orange-400 font-extrabold uppercase tracking-wider">{product.category_name}</span> <h3 className="text-xl font-black text-white mt-1">{product.name}</h3> <p className="text-slate-400 mt-1">{product.subtitle}</p> </div> <div className="flex items-baseline space-x-3 bg-slate-900/80 p-3 rounded-2xl border border-slate-800"> <span className="text-3xl font-black text-orange-400">฿{product.price.toLocaleString()}</span> {product.original_price && ( <span className="text-sm text-slate-500 line-through">฿{product.original_price.toLocaleString()}</span> )} <span className="px-2 py-0.5 rounded bg-orange-500/20 text-orange-400 text-[10px] font-extrabold"> ลด {Math.round((1 - product.price / (product.original_price || product.price * 1.5)) * 100)}% </span> </div> <div className="grid grid-cols-2 gap-2 text-[11px] text-slate-300"> <div className="flex items-center space-x-1.5 p-2 rounded-xl bg-slate-900 border border-slate-800"> <span>🛡️</span> <span>ของแท้ 100% คืนเงิน 2 เท่า</span> </div> <div className="flex items-center space-x-1.5 p-2 rounded-xl bg-slate-900 border border-slate-800"> <span>🚚</span> <span>ส่งฟรีเมื่อสั่งซื้อ 2 ชิ้นขึ้นไป</span> </div> </div> <p className="text-slate-300 leading-relaxed border-t border-slate-800 pt-3"> {product.description} </p> {product.colors && product.colors.length > 0 && ( <div className="space-y-1.5"> <label className="block font-bold text-slate-300">เลือกสี (Color): <span className="text-orange-400">{selectedColor?.name}</span></label> <div className="flex flex-wrap gap-2"> {product.colors.map((c) => ( <button key={c.id} onClick={() => setSelectedColor(c)} className={`px-3 py-1.5 rounded-xl border text-xs font-bold flex items-center space-x-2 cursor-pointer ${ selectedColor?.id === c.id ? 'border-orange-500 bg-orange-500/10 text-white ring-1 ring-orange-500' : 'border-slate-800 bg-slate-950 text-slate-400' }`} > <span className="w-3 h-3 rounded-full border border-white/20" style={{ backgroundColor: c.hex }}></span> <span>{c.name}</span> </button> ))} </div> </div> )} {product.sizes && product.sizes.length > 0 && ( <div className="space-y-1.5"> <label className="block font-bold text-slate-300">เลือกไซส์ (Size): <span className="text-orange-400">{selectedSize}</span></label> <div className="flex flex-wrap gap-2"> {product.sizes.map((sz) => ( <button key={sz} onClick={() => setSelectedSize(sz)} className={`w-10 h-10 rounded-xl border text-xs font-black flex items-center justify-center cursor-pointer ${ selectedSize === sz ? 'border-orange-500 bg-orange-500 text-white shadow-md' : 'border-slate-800 bg-slate-950 text-slate-400' }`} > {sz} </button> ))} </div> </div> )} <div className="flex items-center space-x-3 pt-2"> <div className="flex items-center bg-slate-950 border border-slate-800 rounded-xl p-1"> <button onClick={() => setQty(Math.max(1, qty - 1))} className="p-2 text-slate-400 hover:text-white cursor-pointer"> <Icons.Minus className="w-3.5 h-3.5" /> </button> <span className="px-3 font-bold text-sm text-white">{qty}</span> <button onClick={() => setQty(qty + 1)} className="p-2 text-slate-400 hover:text-white cursor-pointer"> <Icons.Plus className="w-3.5 h-3.5" /> </button> </div> <button onClick={() => { onAddToCart(product, selectedColor || product.colors?.[0], { label: selectedSize }, qty); onClose(); }} className="flex-1 py-3.5 rounded-xl shopee-gradient hover:brightness-110 text-white font-black text-xs shadow-lg cursor-pointer transition-all flex items-center justify-center space-x-2" > <Icons.ShoppingBag className="w-4 h-4" /> <span>เพิ่มลงตะกร้า • ฿{(product.price * qty).toLocaleString()}</span> </button> </div> </div> </div> </div> </div> ); } // --- SHOPEE AUTH MODALS (LOGIN & REGISTER) --- function LoginModal({ isOpen, onClose, onSuccess, onSwitchToRegister, onQuickSwitch }) { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); if (!isOpen) return null; const handleSubmit = async (e) => { e.preventDefault(); setError(''); setLoading(true); try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'login', username, password }) }); const data = await res.json(); if (data.success) { onSuccess(data.user, data.message); onClose(); } else { setError(data.message || 'เข้าสู่ระบบไม่สำเร็จ'); } } catch (err) { setError('เกิดข้อผิดพลาดในการเชื่อมต่อเซิร์ฟเวอร์'); } finally { setLoading(false); } }; return ( <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/85 backdrop-blur-md animate-fade-in"> <div className="bg-slate-900 border border-slate-800 rounded-3xl max-w-md w-full p-6 sm:p-8 space-y-5 shadow-2xl"> <div className="flex items-center justify-between border-b border-slate-800 pb-3"> <div className="flex items-center space-x-3"> <div className="w-10 h-10 rounded-2xl shopee-gradient flex items-center justify-center font-bold text-white shadow-md"> <Icons.Shopee className="w-6 h-6" /> </div> <div> <h3 className="font-black text-lg text-white">เข้าสู่ระบบ Shopee Member Club</h3> <p className="text-xs text-slate-400">รับสิทธิพิเศษ VIP, Shopee Coins & โค้ดส่งฟรี</p> </div> </div> <button onClick={onClose} className="p-2 rounded-xl bg-slate-800 text-slate-400 hover:text-white cursor-pointer"> <Icons.X className="w-5 h-5" /> </button> </div> {/* Quick Demo Switcher */} <QuickDemoSwitcher onQuickSwitch={(tier) => { onQuickSwitch(tier); onClose(); }} /> {error && ( <div className="bg-rose-950/80 border border-rose-500/50 text-rose-200 text-xs p-3 rounded-xl font-bold"> {error} </div> )} <form onSubmit={handleSubmit} className="space-y-3.5 text-xs"> <div> <label className="block font-semibold text-slate-300 mb-1">ชื่อผู้ใช้ หรือ อีเมล</label> <input type="text" required placeholder="Username หรือ Email" value={username} onChange={(e) => setUsername(e.target.value)} className="w-full py-2.5 px-3.5 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> <div> <label className="block font-semibold text-slate-300 mb-1">รหัสผ่าน</label> <input type="password" required placeholder="••••••••" value={password} onChange={(e) => setPassword(e.target.value)} className="w-full py-2.5 px-3.5 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> <button type="submit" disabled={loading} className="w-full py-3.5 rounded-xl shopee-gradient hover:brightness-110 text-white font-black text-xs shadow-lg cursor-pointer transition-all" > {loading ? 'กำลังเข้าสู่ระบบ...' : 'LOG IN เข้าสู่ระบบสมาชิก'} </button> </form> <div className="text-center pt-2 border-t border-slate-800 text-xs text-slate-400"> ยังไม่มีบัญชีสมาชิก?{' '} <button onClick={onSwitchToRegister} className="text-orange-400 font-bold hover:underline cursor-pointer"> สมัครสมาชิก Shopee VIP ที่นี่ </button> </div> </div> </div> ); } function RegisterModal({ isOpen, onClose, onSuccess, onSwitchToLogin, onQuickSwitch }) { const [formData, setFormData] = useState({ username: '', email: '', password: '', fullName: '', phone: '', address: '' }); const [error, setError] = useState(''); const [loading, setLoading] = useState(false); if (!isOpen) return null; const handleChange = (field, val) => { setFormData(prev => ({ ...prev, [field]: val })); }; const handleSubmit = async (e) => { e.preventDefault(); setError(''); setLoading(true); try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'register', ...formData }) }); const data = await res.json(); if (data.success) { onSuccess(data.user, data.message); onClose(); } else { setError(data.message || 'สมัครสมาชิกไม่สำเร็จ'); } } catch (err) { setError('เกิดข้อผิดพลาดในการเชื่อมต่อเซิร์ฟเวอร์'); } finally { setLoading(false); } }; return ( <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/85 backdrop-blur-md animate-fade-in"> <div className="bg-slate-900 border border-slate-800 rounded-3xl max-w-lg w-full p-6 sm:p-8 space-y-4 shadow-2xl max-h-[90vh] overflow-y-auto"> <div className="flex items-center justify-between border-b border-slate-800 pb-3"> <div className="flex items-center space-x-3"> <div className="w-10 h-10 rounded-2xl shopee-gradient flex items-center justify-center font-bold text-white shadow-md"> <Icons.Sparkles className="w-5 h-5" /> </div> <div> <h3 className="font-black text-lg text-white">สมัครสมาชิก Shopee Member Club</h3> <p className="text-xs text-orange-400 font-bold">รับฟรี 100 Shopee Coins + โค้ดส่งฟรีทันที!</p> </div> </div> <button onClick={onClose} className="p-2 rounded-xl bg-slate-800 text-slate-400 hover:text-white cursor-pointer"> <Icons.X className="w-5 h-5" /> </button> </div> <QuickDemoSwitcher onQuickSwitch={(tier) => { onQuickSwitch(tier); onClose(); }} /> {error && ( <div className="bg-rose-950/80 border border-rose-500/50 text-rose-200 text-xs p-3 rounded-xl font-bold"> {error} </div> )} <form onSubmit={handleSubmit} className="space-y-3 text-xs"> <div className="grid grid-cols-2 gap-3"> <div> <label className="block font-semibold text-slate-300 mb-1">Username <span className="text-orange-400">*</span></label> <input type="text" required placeholder="shopee_fan" value={formData.username} onChange={(e) => handleChange('username', e.target.value)} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> <div> <label className="block font-semibold text-slate-300 mb-1">Email <span className="text-orange-400">*</span></label> <input type="email" required placeholder="user@example.com" value={formData.email} onChange={(e) => handleChange('email', e.target.value)} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> </div> <div className="grid grid-cols-2 gap-3"> <div> <label className="block font-semibold text-slate-300 mb-1">รหัสผ่าน <span className="text-orange-400">*</span></label> <input type="password" required placeholder="อย่างน้อย 6 ตัวอักษร" value={formData.password} onChange={(e) => handleChange('password', e.target.value)} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> <div> <label className="block font-semibold text-slate-300 mb-1">ชื่อ-นามสกุล <span className="text-orange-400">*</span></label> <input type="text" required placeholder="สมชาย ใจดี" value={formData.fullName} onChange={(e) => handleChange('fullName', e.target.value)} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> </div> <div> <label className="block font-semibold text-slate-300 mb-1">เบอร์โทรศัพท์ (10 หลัก) <span className="text-orange-400">*</span></label> <input type="tel" required placeholder="0812345678" value={formData.phone} onChange={(e) => handleChange('phone', e.target.value)} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" /> </div> <div> <label className="block font-semibold text-slate-300 mb-1">ที่อยู่จัดส่งสินค้า</label> <textarea rows={2} placeholder="บ้านเลขที่ ถนน แขวง/ตำบล เขต/อำเภอ จังหวัด รหัสไปรษณีย์" value={formData.address} onChange={(e) => handleChange('address', e.target.value)} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100 focus:outline-none focus:border-orange-500" ></textarea> </div> <button type="submit" disabled={loading} className="w-full py-3 rounded-xl shopee-gradient hover:brightness-110 text-white font-black text-xs shadow-lg cursor-pointer transition-all mt-2" > {loading ? 'กำลังบันทึกข้อมูล...' : 'REGISTER NOW (สมัครสมาชิกรับ 100 Coins)'} </button> </form> <div className="text-center pt-2 border-t border-slate-800 text-xs text-slate-400"> มีบัญชีสมาชิกอยู่แล้ว?{' '} <button onClick={onSwitchToLogin} className="text-orange-400 font-bold hover:underline cursor-pointer"> เข้าสู่ระบบที่นี่ </button> </div> </div> </div> ); } // --- MAIN REACT APP --- function App() { const [currentUser, setCurrentUser] = useState(window.PHP_USER || null); const [userOrders, setUserOrders] = useState([]); const [coinLogs, setCoinLogs] = useState([]); const [fashionCatalog, setFashionCatalog] = useState(window.PHP_FASHION_CATALOG || []); const [selectedCategory, setSelectedCategory] = useState('all'); const [isLoginModalOpen, setIsLoginModalOpen] = useState(false); const [isRegisterModalOpen, setIsRegisterModalOpen] = useState(false); const [isMemberHubOpen, setIsMemberHubOpen] = useState(false); const [memberHubTab, setMemberHubTab] = useState('tier'); const [isAddProductOpen, setIsAddProductOpen] = useState(false); const [selectedProductModal, setSelectedProductModal] = useState(null); const [cart, setCart] = useState([]); const [selectedVoucher, setSelectedVoucher] = useState(null); const [useCoins, setUseCoins] = useState(false); const [totalOrderedCount, setTotalOrderedCount] = useState(STORE_CONFIG.totalOrdered || 284); const [remainingStock, setRemainingStock] = useState(STORE_CONFIG.remainingStock || 66); const [orders, setOrders] = useState(window.PHP_INITIAL_ORDERS || []); const [formData, setFormData] = useState({ fullName: currentUser ? (currentUser.full_name || '') : '', phone: currentUser ? (currentUser.phone || '') : '', address: currentUser ? (currentUser.address || '') : '', note: '', paymentMethod: 'promptpay' }); const [isSubmitting, setIsSubmitting] = useState(false); const [isCartOpen, setIsCartOpen] = useState(false); const [confirmedOrder, setConfirmedOrder] = useState(null); const [toasts, setToasts] = useState([]); const addToast = (message, type = 'success') => { const id = Date.now(); setToasts(prev => [...prev, { id, message, type }]); setTimeout(() => { setToasts(prev => prev.filter(t => t.id !== id)); }, 3500); }; const refreshUserData = async () => { try { const res = await fetch('index.php?action=get_user'); const data = await res.json(); if (data.success) { setCurrentUser(data.user); setUserOrders(data.orders || []); setCoinLogs(data.coinLogs || []); } } catch (err) {} }; useEffect(() => { if (currentUser) { setFormData(prev => ({ ...prev, fullName: currentUser.full_name || prev.fullName, phone: currentUser.phone || prev.phone, address: currentUser.address || prev.address })); refreshUserData(); } }, [currentUser?.id]); const handleQuickSwitchTier = async (tierKey) => { try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'quick_login', tier: tierKey }) }); const data = await res.json(); if (data.success) { setCurrentUser(data.user); addToast(data.message, 'success'); refreshUserData(); } else { addToast(data.message || 'สลับบัญชีไม่สำเร็จ', 'error'); } } catch (err) { addToast('เชื่อมต่อเซิร์ฟเวอร์ล้มเหลว', 'error'); } }; const handleClaimCoins = async () => { try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'claim_daily_coins' }) }); const data = await res.json(); if (data.success) { setCurrentUser(data.user); addToast(`เช็คอินสำเร็จ! รับ +${data.earnedCoins} Shopee Coins (วันที่ ${data.streak}/7) 🎉`, 'success'); refreshUserData(); } else { addToast(data.message, 'error'); } } catch (err) { addToast('เกิดข้อผิดพลาดในการรับเหรียญ', 'error'); } }; const handleSpinWheel = async () => { try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'lucky_spin' }) }); const data = await res.json(); if (data.success) { setCurrentUser(data.user); addToast(data.message, 'success'); refreshUserData(); return data; } else { addToast(data.message, 'error'); return null; } } catch (err) { addToast('เกิดข้อผิดพลาดในการหมุนวงล้อ', 'error'); return null; } }; const handleClaimMission = async (missionId) => { try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'claim_mission', missionId }) }); const data = await res.json(); if (data.success) { setCurrentUser(data.user); addToast(data.message, 'success'); refreshUserData(); } else { addToast(data.message, 'error'); } } catch (err) { addToast('เกิดข้อผิดพลาดในการรับรางวัลภารกิจ', 'error'); } }; const handleCollectVoucher = async (code) => { try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'collect_voucher', voucherCode: code }) }); const data = await res.json(); if (data.success) { setCurrentUser(data.user); addToast('เก็บโค้ดส่วนลดเข้ากระเป๋าสำเร็จ!', 'success'); refreshUserData(); } else { addToast(data.message, 'error'); } } catch (err) { addToast('เกิดข้อผิดพลาดในการเก็บโค้ด', 'error'); } }; const handleLogout = async () => { try { await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'logout' }) }); setCurrentUser(null); setUserOrders([]); setCoinLogs([]); setIsMemberHubOpen(false); addToast('ออกจากระบบเรียบร้อยแล้ว'); } catch (err) {} }; const handleAddToCart = (product, color, size, quantity = 1) => { const cartItem = { id: `${product.id}-${color.id}-${size.label}`, productId: product.id, title: product.name, color: color, size: size, price: product.price, quantity: quantity, image: product.image }; setCart(prev => { const existing = prev.find(i => i.id === cartItem.id); if (existing) { return prev.map(i => i.id === cartItem.id ? { ...i, quantity: i.quantity + quantity } : i); } return [...prev, cartItem]; }); addToast(`เพิ่ม "${product.name}" ลงตะกร้าแล้ว!`, 'success'); }; const handleUpdateCartQty = (id, delta) => { setCart(prev => prev.map(item => { if (item.id === id) { const newQty = item.quantity + delta; return newQty > 0 ? { ...item, quantity: newQty } : null; } return item; }).filter(Boolean)); }; const handleRemoveCartItem = (id) => { setCart(prev => prev.filter(item => item.id !== id)); }; // Cart Calculations const cartSubtotal = cart.reduce((sum, item) => sum + (item.price * item.quantity), 0); const totalCartCount = cart.reduce((sum, item) => sum + item.quantity, 0); const shippingCost = totalCartCount >= STORE_CONFIG.freeShippingMinQty || selectedVoucher === 'SHOPEEFREE' ? 0 : STORE_CONFIG.shippingFee; let voucherDiscount = 0; if (selectedVoucher === 'MALL10') { voucherDiscount = Math.round(cartSubtotal * 0.10); } else if (selectedVoucher === 'NEWUSER50') { voucherDiscount = Math.min(50, cartSubtotal); } else if (selectedVoucher === 'BDAY100') { voucherDiscount = Math.min(100, cartSubtotal); } function intval(v) { return parseInt(v, 10) || 0; } const availableCoins = currentUser ? intval(currentUser.coins || 0) : 0; const maxCoinsDiscount = Math.min(availableCoins, Math.round(cartSubtotal * 0.25)); const coinsDiscount = useCoins ? maxCoinsDiscount : 0; const grandTotal = Math.max(0, cartSubtotal + shippingCost - voucherDiscount - coinsDiscount); const handleCheckoutSubmit = async (e) => { e.preventDefault(); if (cart.length === 0) { addToast('ตะกร้าสินค้าว่างเปล่า', 'error'); return; } if (!formData.fullName.trim() || !formData.phone.trim() || !formData.address.trim()) { addToast('กรุณากรอกข้อมูลการจัดส่งให้ครบถ้วน', 'error'); return; } setIsSubmitting(true); try { const res = await fetch('index.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ fullName: formData.fullName, phone: formData.phone, address: formData.address, note: formData.note, paymentMethod: formData.paymentMethod, voucherCode: selectedVoucher || '', coinsUsed: coinsDiscount, items: cart }) }); const data = await res.json(); if (res.ok && data.success) { setConfirmedOrder(data.order); if (data.user) setCurrentUser(data.user); if (data.totalOrdered) setTotalOrderedCount(data.totalOrdered); if (data.remainingStock) setRemainingStock(data.remainingStock); if (data.allOrders) setOrders(data.allOrders); setCart([]); setIsCartOpen(false); addToast('สั่งซื้อสำเร็จ! ได้รับ Shopee Coins คืนเข้ากระเป๋า 🎉', 'success'); refreshUserData(); } else { addToast(data.message || 'เกิดข้อผิดพลาดในการบันทึกคำสั่งซื้อ', 'error'); } } catch (err) { addToast('เชื่อมต่อเซิร์ฟเวอร์ล้มเหลว', 'error'); } finally { setIsSubmitting(false); } }; const categories = [ { id: 'all', name: '🌟 ทั้งหมด (All Catalog)' }, { id: 't-shirts', name: '👕 เสื้อยืด (T-Shirts)' }, { id: 'hoodies', name: '🧥 เสื้อกันหนาว & แจ็คเก็ต' }, { id: 'pants', name: '👖 กางเกง (Pants)' }, { id: 'accessories', name: '🧢 แอคเซสเซอรี่' } ]; const filteredCatalog = useMemo(() => { if (selectedCategory === 'all') return fashionCatalog; return fashionCatalog.filter(p => p.category === selectedCategory); }, [fashionCatalog, selectedCategory]); return ( <div className="min-h-screen flex flex-col bg-[#080d1a] text-slate-100 selection:bg-orange-500 selection:text-white"> {/* SHOPEE TOP BAR BANNER */} <div className="shopee-gradient text-white text-xs font-semibold py-2 px-4 sm:px-8 shadow-md"> <div className="max-w-[1600px] mx-auto flex flex-col sm:flex-row items-center justify-between gap-2 text-center sm:text-left"> <div className="flex items-center space-x-2"> <span className="bg-white/20 px-2 py-0.5 rounded-full text-[10px] font-black uppercase tracking-wider flex items-center gap-1"> <Icons.Shopee className="w-3.5 h-3.5" /> Shopee Mall Official </span> <span>🔥 สินค้าแฟชั่นพรีเมียม <strong>รับประกันของแท้ 100%</strong> • คืนเงิน 2 เท่า • ส่งฟรีทั่วไทย</span> </div> <div className="flex items-center space-x-3 text-[11px] font-bold"> {currentUser ? ( <button onClick={() => { setMemberHubTab('streak'); setIsMemberHubOpen(true); }} className="bg-black/25 hover:bg-black/40 px-3 py-1 rounded-full flex items-center space-x-1.5 transition-all cursor-pointer" > <span>🪙 เหรียญของคุณ:</span> <strong className="text-amber-300">{currentUser.coins} Coins</strong> <span className="text-[10px] bg-amber-400 text-slate-950 font-black px-1.5 rounded-full">เช็คอิน</span> </button> ) : ( <button onClick={() => setIsLoginModalOpen(true)} className="bg-black/20 hover:bg-black/30 px-2.5 py-1 rounded-full cursor-pointer text-amber-300 font-bold" > ⚡ สมัครสมาชิกรับฟรี 100 Coins </button> )} <span>Flash Express / Kerry COD</span> </div> </div> </div> {/* MAIN HEADER */} <header className="sticky top-0 z-40 bg-[#0c1322]/90 backdrop-blur-xl border-b border-slate-800 shadow-xl"> <div className="max-w-[1600px] mx-auto px-4 sm:px-8 h-20 flex items-center justify-between"> {/* Logo & Shopee Mall Badge */} <div className="flex items-center space-x-3"> <div className="w-11 h-11 rounded-2xl shopee-gradient flex items-center justify-center shadow-lg shadow-orange-500/25 font-black text-2xl text-white"> S </div> <div> <div className="flex items-center space-x-2"> <span className="text-xl font-black tracking-tight text-white"> APEX <span className="text-orange-400">STUDIO</span> </span> <span className="px-2 py-0.5 rounded bg-rose-600 text-white text-[9px] font-black tracking-wider"> MALL </span> </div> <p className="text-[10px] text-slate-400 font-semibold">Official Shopee Flagship Store</p> </div> </div> {/* Navigation Links */} <nav className="hidden lg:flex items-center space-x-5 text-xs font-bold text-slate-300"> <a href="#catalog" className="hover:text-orange-400 transition-colors">สินค้าทั้งหมด</a> <a href="#flash-sale" className="text-orange-400 hover:text-orange-300 flex items-center space-x-1"> <Icons.Sparkles className="w-3.5 h-3.5" /> <span>Flash Sale</span> </a> {/* Shopee Member Club Direct Nav Buttons */} <button onClick={() => { setMemberHubTab('tier'); if (!currentUser) setIsLoginModalOpen(true); else setIsMemberHubOpen(true); }} className="px-3 py-1.5 rounded-xl bg-purple-500/10 border border-purple-500/30 text-purple-300 hover:bg-purple-600 hover:text-white font-bold transition-all flex items-center space-x-1.5 cursor-pointer" > <span>👑</span> <span>Shopee VIP Club</span> </button> <button onClick={() => { setMemberHubTab('spin'); if (!currentUser) setIsLoginModalOpen(true); else setIsMemberHubOpen(true); }} className="px-3 py-1.5 rounded-xl bg-amber-500/10 border border-amber-500/30 text-amber-300 hover:bg-amber-500 hover:text-slate-950 font-bold transition-all flex items-center space-x-1.5 cursor-pointer" > <span>🎡</span> <span>วงล้อลุ้นโชค</span> </button> <button onClick={() => { setMemberHubTab('missions'); if (!currentUser) setIsLoginModalOpen(true); else setIsMemberHubOpen(true); }} className="px-3 py-1.5 rounded-xl bg-rose-500/10 border border-rose-500/30 text-rose-300 hover:bg-rose-600 hover:text-white font-bold transition-all flex items-center space-x-1.5 cursor-pointer" > <span>🎯</span> <span>ภารกิจสะสมเหรียญ</span> </button> <button onClick={() => setIsAddProductOpen(true)} className="px-3 py-1.5 rounded-xl bg-slate-800 hover:bg-slate-700 border border-slate-700 text-slate-300 font-bold transition-all flex items-center space-x-1.5 cursor-pointer" > <Icons.Upload className="w-3.5 h-3.5 text-orange-400" /> <span>+ เพิ่มสินค้า</span> </button> </nav> {/* Header Right Actions */} <div className="flex items-center space-x-3"> {currentUser ? ( <button onClick={() => { setMemberHubTab('tier'); setIsMemberHubOpen(true); }} className="px-3.5 py-2 rounded-2xl bg-slate-800 hover:bg-slate-700 border border-orange-500/50 text-orange-300 font-bold text-xs flex items-center space-x-2 cursor-pointer shadow-md" > {currentUser.avatar ? ( <img src={currentUser.avatar} alt="User" className="w-6 h-6 rounded-full object-cover border border-orange-400" /> ) : ( <div className="w-6 h-6 rounded-full shopee-gradient text-white flex items-center justify-center font-black text-[10px]"> {currentUser.username ? currentUser.username[0].toUpperCase() : 'U'} </div> )} <span className="hidden sm:inline">{currentUser.full_name || currentUser.username}</span> <span className="bg-orange-500 text-white text-[9px] font-black px-1.5 py-0.5 rounded shadow"> {currentUser.membership_tier ? currentUser.membership_tier.toUpperCase() : 'VIP'} </span> </button> ) : ( <div className="flex items-center space-x-2"> <button onClick={() => setIsLoginModalOpen(true)} className="px-3.5 py-2 rounded-xl bg-slate-800 hover:bg-slate-700 text-slate-200 font-bold text-xs cursor-pointer border border-slate-700" > เข้าสู่ระบบ </button> <button onClick={() => setIsRegisterModalOpen(true)} className="px-3.5 py-2 rounded-xl shopee-gradient text-white font-black text-xs cursor-pointer shadow-md" > สมัครสมาชิก VIP </button> </div> )} {/* Cart Trigger */} <button onClick={() => setIsCartOpen(true)} className="relative touch-target px-4 py-2.5 rounded-2xl bg-slate-800 hover:bg-slate-700 border border-slate-700/80 transition-all flex items-center space-x-2.5 group shadow-lg cursor-pointer" > <div className="relative"> <Icons.ShoppingBag className="w-5 h-5 text-orange-400 group-hover:scale-110 transition-transform" /> {totalCartCount > 0 && ( <span className="absolute -top-2 -right-2 shopee-gradient text-white font-black text-[10px] w-5 h-5 rounded-full flex items-center justify-center shadow-md"> {totalCartCount} </span> )} </div> <div className="hidden sm:flex flex-col text-left text-xs"> <span className="text-slate-400 text-[10px]">ตะกร้า</span> <span className="text-orange-400 font-black">฿{cartSubtotal.toLocaleString()}</span> </div> </button> </div> </div> </header> {/* SHOPEE MEMBER VIP CLUB HERO BANNER */} <section className="bg-gradient-to-r from-orange-950/40 via-purple-950/30 to-slate-950 border-b border-slate-800 py-6 px-4 sm:px-8"> <div className="max-w-[1600px] mx-auto flex flex-col md:flex-row items-center justify-between gap-6"> <div className="w-full md:w-2/3 space-y-3"> <div className="flex items-center space-x-2.5"> <span className="px-3 py-1 rounded-full shopee-gradient text-white text-xs font-black uppercase tracking-wider flex items-center space-x-1.5 shadow-lg"> <span>👑</span> <span>SHOPEE MEMBER CLUB VIP</span> </span> <span className="text-xs text-amber-300 font-bold"> สมาชิกลอยัลตี้รับ Shopee Coins & สิทธิพิเศษสูงสุด 4 ระดับ </span> </div> <h1 className="text-2xl sm:text-3xl font-black text-white"> ระบบสมาชิกสไตล์ Shopee: Classic, Silver, Gold & Platinum Super VIP </h1> <p className="text-xs sm:text-sm text-slate-300 leading-relaxed"> ช้อปเสื้อผ้าแฟชั่นสตรีท APEX STUDIO สะสมยอดออเดอร์เพื่อเลื่อนระดับ VIP รับเงินคืน Coins สูงสุด 10%, โค้ดส่งฟรี x8 โค้ดต่อเดือน, เช็คอิน 7 วัน และของขวัญวันเกิด </p> {/* 1-Click Quick Tier Switcher Bar in Hero */} <div className="pt-2"> <QuickDemoSwitcher onQuickSwitch={handleQuickSwitchTier} /> </div> </div> {/* Shopee VIP Quick Actions Box */} <div className="flex flex-col gap-2.5 w-full md:w-auto shrink-0 text-xs"> <div onClick={() => { setMemberHubTab('streak'); if (!currentUser) setIsLoginModalOpen(true); else setIsMemberHubOpen(true); }} className="glass-card p-3.5 rounded-2xl border border-amber-500/40 hover:border-amber-400 cursor-pointer flex items-center space-x-3 transition-all hover:scale-102" > <span className="text-2xl">📅</span> <div> <p className="font-extrabold text-white">เช็คอิน 7 วันรับเหรียญ</p> <p className="text-[10px] text-amber-400 font-semibold">รับสูงสุด +50 Coins & Super Box</p> </div> </div> <div onClick={() => { setMemberHubTab('spin'); if (!currentUser) setIsLoginModalOpen(true); else setIsMemberHubOpen(true); }} className="glass-card p-3.5 rounded-2xl border border-purple-500/40 hover:border-purple-400 cursor-pointer flex items-center space-x-3 transition-all hover:scale-102" > <span className="text-2xl">🎡</span> <div> <p className="font-extrabold text-white">วงล้อลุ้นโชค Shopee Rewards</p> <p className="text-[10px] text-purple-300 font-semibold">หมุนฟรีลุ้นรับ 100 Coins & โค้ดลด</p> </div> </div> </div> </div> </section> {/* FLASH SALE & CAMPAIGN BANNER */} <section id="flash-sale" className="bg-[#0b101d] border-b border-slate-800 py-6 px-4 sm:px-8"> <div className="max-w-[1600px] mx-auto flex flex-col md:flex-row items-center justify-between gap-6"> <div className="w-full md:w-2/3 space-y-2.5"> <div className="flex items-center space-x-3"> <span className="px-2.5 py-0.5 rounded-full shopee-gradient text-white text-[11px] font-black uppercase tracking-wider flex items-center space-x-1 shadow"> <Icons.Sparkles className="w-3 h-3" /> <span>FLASH SALE ⚡ ลดสูงสุด 50%</span> </span> <div className="flex items-center space-x-1 text-xs text-orange-400 font-bold"> <span>นับถอยหลัง: <strong>02 : 18 : 45</strong></span> </div> </div> <h2 className="text-xl sm:text-2xl font-black text-white"> Apex Heavyweight Oversized Tee 220 GSM Bio-washed </h2> <div className="space-y-1 max-w-xl"> <div className="flex justify-between text-xs font-bold"> <span className="text-slate-300">ขายแล้ว {totalOrderedCount} / {STORE_CONFIG.campaignGoal || 350} ชิ้น</span> <span className="text-orange-400">คงเหลือ {remainingStock} ชิ้น!</span> </div> <div className="w-full h-2.5 bg-slate-800 rounded-full overflow-hidden p-0.5 border border-slate-700"> <div className="h-full shopee-gradient rounded-full transition-all duration-700" style={{ width: `${Math.min(100, (totalOrderedCount / (STORE_CONFIG.campaignGoal || 350)) * 100)}%` }} ></div> </div> </div> </div> <div className="flex gap-3 text-xs"> <div className="glass-card p-3 rounded-2xl border border-slate-800 flex items-center space-x-2"> <span className="text-xl">🛡️</span> <div> <p className="font-bold text-white">Shopee Mall แท้ 100%</p> <p className="text-[10px] text-slate-400">คืนเงิน 2 เท่าหากไม่แท้</p> </div> </div> </div> </div> </section> {/* FASHION PRODUCTS CATALOG */} <main id="catalog" className="flex-1 max-w-[1600px] w-full mx-auto px-4 sm:px-8 py-10 space-y-8"> <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 border-b border-slate-800 pb-4"> <div> <h2 className="text-2xl sm:text-3xl font-black text-white tracking-tight flex items-center space-x-2"> <span>สินค้าทั้งหมดในร้าน</span> <span className="text-orange-400 text-sm font-bold">({filteredCatalog.length} รายการ)</span> </h2> <p className="text-xs text-slate-400 mt-0.5">ภาพถ่ายสินค้าจริงและสวมใส่บนตัวแบบ (Real Product Photography)</p> </div> <div className="flex items-center space-x-2"> <button onClick={() => setIsAddProductOpen(true)} className="px-4 py-2 rounded-2xl shopee-gradient text-white text-xs font-black flex items-center space-x-1.5 shadow-lg cursor-pointer" > <Icons.Plus className="w-4 h-4" /> <span>เพิ่มสินค้าใหม่ (อัปโหลดรูปภาพ)</span> </button> </div> </div> {/* Category Filter Tabs */} <div className="flex flex-wrap gap-2"> {categories.map((cat) => ( <button key={cat.id} onClick={() => setSelectedCategory(cat.id)} className={`px-4 py-2 rounded-2xl text-xs font-extrabold transition-all cursor-pointer border ${ selectedCategory === cat.id ? 'shopee-gradient text-white border-orange-500 shadow-lg shadow-orange-500/20 scale-105' : 'bg-slate-900 text-slate-400 border-slate-800 hover:text-white' }`} > {cat.name} </button> ))} </div> {/* Product Grid */} <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> {filteredCatalog.map((prod) => ( <div key={prod.id} className="group glass-card rounded-3xl overflow-hidden border border-slate-800/90 hover:border-orange-500/60 transition-all duration-300 hover:shadow-2xl hover:-translate-y-1 flex flex-col justify-between" > <div> <div className="relative aspect-[4/5] overflow-hidden bg-slate-950"> <img src={prod.image} alt={prod.name} className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500" /> <div className="absolute top-3 left-3 flex flex-col space-y-1"> <span className={`px-2.5 py-0.5 rounded-md text-[10px] font-black text-white shadow-md ${prod.badge_color || 'bg-rose-600'}`}> {prod.badge || 'SHOPEE MALL'} </span> </div> <div className="absolute top-3 right-3 bg-slate-950/80 backdrop-blur-md px-2.5 py-1 rounded-full border border-slate-800 text-[10px] font-bold text-amber-400 flex items-center space-x-1"> <span>★ {prod.rating}</span> <span className="text-slate-500">({prod.reviews_count})</span> </div> <div className="absolute bottom-3 left-3 bg-slate-950/85 backdrop-blur-md px-2.5 py-1 rounded-lg text-[10px] text-slate-300 font-semibold border border-slate-800"> ขายแล้ว {prod.sold_count || 140} ชิ้น </div> </div> <div className="p-4 space-y-2"> <span className="text-[10px] text-orange-400 font-extrabold uppercase tracking-wider">{prod.category_name}</span> <h3 className="font-extrabold text-sm text-white group-hover:text-orange-400 transition-colors line-clamp-1"> {prod.name} </h3> <p className="text-[11px] text-slate-400 line-clamp-2">{prod.subtitle}</p> <div className="flex items-baseline space-x-2 pt-1"> <span className="text-lg font-black text-white">฿{prod.price.toLocaleString()}</span> {prod.original_price && ( <span className="text-xs text-slate-500 line-through">฿{prod.original_price.toLocaleString()}</span> )} <span className="text-[10px] text-orange-400 font-bold"> - {Math.round((1 - prod.price / (prod.original_price || prod.price * 1.5)) * 100)}% </span> </div> </div> </div> <div className="p-4 pt-0 flex gap-2"> <button onClick={() => setSelectedProductModal(prod)} className="flex-1 py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 text-white font-bold text-xs cursor-pointer text-center transition-all" > ดูรูปจริง / เลือกไซส์ </button> <button onClick={() => handleAddToCart(prod, prod.colors?.[0] || COLOR_OPTIONS[0], { label: prod.sizes?.[0] || 'M' }, 1)} className="p-2.5 rounded-xl shopee-gradient hover:brightness-110 text-white font-bold cursor-pointer transition-all shadow-md" title="เพิ่มลงตะกร้า" > <Icons.ShoppingBag className="w-4 h-4" /> </button> </div> </div> ))} </div> </main> {/* CART DRAWER */} {isCartOpen && ( <div className="fixed inset-0 z-50 overflow-hidden"> <div onClick={() => setIsCartOpen(false)} className="absolute inset-0 bg-slate-950/80 backdrop-blur-md"></div> <div className="fixed inset-y-0 right-0 max-w-full flex pl-10"> <div className="w-screen max-w-md bg-[#111827] border-l border-slate-800 shadow-2xl flex flex-col justify-between animate-slide-in-right"> <div className="p-5 border-b border-slate-800 flex items-center justify-between bg-slate-950/60"> <div className="flex items-center space-x-2"> <Icons.ShoppingBag className="w-5 h-5 text-orange-400" /> <h3 className="font-bold text-slate-100 text-base">ตะกร้าสินค้า Shopee ({totalCartCount})</h3> </div> <button onClick={() => setIsCartOpen(false)} className="p-2 rounded-xl text-slate-400 hover:text-white bg-slate-800 cursor-pointer"> <Icons.X className="w-5 h-5" /> </button> </div> <div className="flex-1 overflow-y-auto p-5 space-y-5"> {cart.length === 0 ? ( <div className="py-16 text-center space-y-3"> <p className="font-bold text-slate-300 text-sm">ยังไม่มีสินค้าในตะกร้า</p> <button onClick={() => setIsCartOpen(false)} className="px-5 py-2.5 rounded-xl shopee-gradient text-white font-bold text-xs cursor-pointer"> เริ่มเลือกช้อปสินค้า </button> </div> ) : ( <div className="space-y-4"> {cart.map((item) => ( <div key={item.id} className="p-3.5 rounded-2xl bg-slate-800/60 border border-slate-800 flex space-x-3 items-center"> <div className="w-14 h-16 rounded-xl overflow-hidden bg-slate-950 border border-slate-700 shrink-0"> {item.image ? ( <img src={item.image} alt={item.title} className="w-full h-full object-cover" /> ) : ( <div className="w-full h-full flex items-center justify-center font-bold text-xs text-white" style={{ backgroundColor: item.color.hex }}> {item.size.label} </div> )} </div> <div className="flex-1 min-w-0 space-y-1"> <h4 className="font-bold text-slate-200 text-xs truncate">{item.title}</h4> <p className="text-[11px] text-slate-400">ไซส์: {item.size.label} • ฿{item.price}</p> <div className="flex items-center space-x-1.5 bg-slate-900 px-2 py-0.5 rounded-lg border border-slate-700 w-fit"> <button onClick={() => handleUpdateCartQty(item.id, -1)} className="w-5 h-5 text-slate-300 cursor-pointer"><Icons.Minus className="w-3 h-3" /></button> <span className="text-xs font-bold text-orange-400 w-5 text-center">{item.quantity}</span> <button onClick={() => handleUpdateCartQty(item.id, 1)} className="w-5 h-5 text-slate-300 cursor-pointer"><Icons.Plus className="w-3 h-3" /></button> </div> </div> <div className="text-right space-y-2"> <span className="font-bold text-sm text-slate-100 block">฿{(item.price * item.quantity).toLocaleString()}</span> <button onClick={() => handleRemoveCartItem(item.id)} className="text-rose-400 p-1 text-xs cursor-pointer"><Icons.Trash className="w-4 h-4" /></button> </div> </div> ))} {/* SHOPEE VOUCHER SELECTOR */} <div className="p-3.5 rounded-2xl bg-orange-950/20 border border-orange-500/30 space-y-2"> <div className="flex items-center justify-between text-xs"> <span className="font-bold text-orange-400 flex items-center space-x-1"> <Icons.Tag className="w-3.5 h-3.5" /> <span>โค้ดส่วนลด Shopee</span> </span> <span className="text-[10px] text-slate-400">เลือกใช้โค้ด</span> </div> <select value={selectedVoucher || ''} onChange={(e) => setSelectedVoucher(e.target.value)} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-xs text-white" > <option value="">-- ไม่ใช้โค้ดส่วนลด --</option> <option value="SHOPEEFREE">SHOPEEFREE - ส่งฟรีขั้นต่ำ 0.- (ลด ฿50)</option> <option value="MALL10">MALL10 - ส่วนลด 10% Shopee Mall</option> <option value="NEWUSER50">NEWUSER50 - ลด 50.- สมาชิกใหม่</option> <option value="BDAY100">BDAY100 - ของขวัญวันเกิด VIP ลด 100.-</option> </select> </div> {/* SHOPEE COINS REDEMPTION */} {currentUser && availableCoins > 0 && ( <div className="p-3.5 rounded-2xl bg-amber-950/20 border border-amber-500/30 flex items-center justify-between text-xs"> <div className="flex items-center space-x-2"> <span className="text-base">🪙</span> <div> <p className="font-bold text-amber-300">ใช้ Shopee Coins</p> <p className="text-[10px] text-slate-400">มี {availableCoins} Coins (ลดได้สูงสุด ฿{maxCoinsDiscount})</p> </div> </div> <input type="checkbox" checked={useCoins} onChange={(e) => setUseCoins(e.target.checked)} className="w-5 h-5 accent-orange-500 cursor-pointer" /> </div> )} {/* CHECKOUT FORM */} <div className="border-t border-slate-800 pt-4 space-y-3"> <h4 className="font-extrabold text-xs text-slate-200 flex items-center space-x-1.5"> <Icons.User className="w-4 h-4 text-orange-400" /> <span>ข้อมูลผู้รับและที่อยู่จัดส่ง</span> </h4> <form id="checkout-form" onSubmit={handleCheckoutSubmit} className="space-y-3 text-xs"> <div> <label className="block text-slate-400 mb-1">ชื่อ-นามสกุล <span className="text-orange-400">*</span></label> <input type="text" required value={formData.fullName} onChange={(e) => setFormData({ ...formData, fullName: e.target.value })} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100" /> </div> <div> <label className="block text-slate-400 mb-1">เบอร์โทรศัพท์ <span className="text-orange-400">*</span></label> <input type="tel" required value={formData.phone} onChange={(e) => setFormData({ ...formData, phone: e.target.value })} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100" /> </div> <div> <label className="block text-slate-400 mb-1">ที่อยู่จัดส่ง <span className="text-orange-400">*</span></label> <textarea rows={2} required value={formData.address} onChange={(e) => setFormData({ ...formData, address: e.target.value })} className="w-full py-2 px-3 rounded-xl bg-slate-950 border border-slate-800 text-slate-100" ></textarea> </div> <div> <label className="block text-slate-400 mb-1">ช่องทางชำระเงิน</label> <div className="grid grid-cols-2 gap-2"> <button type="button" onClick={() => setFormData({ ...formData, paymentMethod: 'promptpay' })} className={`p-2.5 rounded-xl border text-left cursor-pointer ${ formData.paymentMethod === 'promptpay' ? 'bg-orange-500/20 border-orange-500 text-orange-300' : 'bg-slate-950 border-slate-800 text-slate-400' }`} > <p className="font-bold text-xs text-white">Mobile Banking</p> <p className="text-[10px] text-slate-400">QR PromptPay</p> </button> <button type="button" onClick={() => setFormData({ ...formData, paymentMethod: 'cod' })} className={`p-2.5 rounded-xl border text-left cursor-pointer ${ formData.paymentMethod === 'cod' ? 'bg-orange-500/20 border-orange-500 text-orange-300' : 'bg-slate-950 border-slate-800 text-slate-400' }`} > <p className="font-bold text-xs text-white">เก็บเงินปลายทาง</p> <p className="text-[10px] text-slate-400">COD จ่ายตอนรับของ</p> </button> </div> </div> </form> </div> </div> )} </div> {cart.length > 0 && ( <div className="p-5 border-t border-slate-800 bg-slate-950/90 space-y-3"> <div className="space-y-1 text-xs text-slate-300"> <div className="flex justify-between"><span>ยอดรวมสินค้า</span><span>฿{cartSubtotal.toLocaleString()}</span></div> <div className="flex justify-between"><span>ค่าจัดส่ง</span><span>{shippingCost === 0 ? 'ฟรี (FREE)' : `฿${shippingCost}`}</span></div> {voucherDiscount > 0 && ( <div className="flex justify-between text-orange-400"><span>โค้ดส่วนลด</span><span>-฿{voucherDiscount}</span></div> )} {coinsDiscount > 0 && ( <div className="flex justify-between text-amber-400"><span>ส่วนลด Shopee Coins</span><span>-฿{coinsDiscount}</span></div> )} <div className="flex justify-between font-bold text-white pt-2 border-t border-slate-800 text-sm"> <span>ยอดชำระสุทธิ</span><span className="text-orange-400 text-xl font-black">฿{grandTotal.toLocaleString()}</span> </div> </div> <button type="submit" form="checkout-form" disabled={isSubmitting} className="w-full py-3.5 rounded-xl shopee-gradient text-white font-black text-sm shadow-xl cursor-pointer disabled:opacity-40" > {isSubmitting ? 'กำลังดำเนินการ...' : 'สั่งซื้อสินค้า (PLACE ORDER)'} </button> </div> )} </div> </div> </div> )} {/* CONFIRMED ORDER POPUP */} {confirmedOrder && ( <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-950/85 backdrop-blur-md"> <div className="bg-slate-900 border border-slate-800 rounded-3xl max-w-lg w-full p-6 sm:p-8 space-y-5 shadow-2xl my-8 animate-fade-in"> <div className="text-center space-y-2"> <div className="w-14 h-14 rounded-full bg-emerald-500/20 text-emerald-400 border border-emerald-500/40 mx-auto flex items-center justify-center shadow-lg"> <Icons.Check className="w-7 h-7" /> </div> <h3 className="text-2xl font-black text-white">สั่งซื้อสินค้าสำเร็จ!</h3> <p className="text-xs text-orange-400 font-semibold">{confirmedOrder.customer.paymentLabel}</p> <div className="inline-block px-3 py-1 rounded-full bg-slate-800 border border-slate-700 text-orange-400 font-mono font-bold text-xs"> เลขที่ออเดอร์: {confirmedOrder.orderId} </div> </div> {confirmedOrder.customer.paymentMethod === 'promptpay' ? ( <div className="glass-card p-5 rounded-2xl text-center space-y-3 border border-slate-800"> <p className="text-xs font-bold text-orange-400">สแกน QR Code เพื่อชำระเงินผ่าน Mobile Banking</p> <div className="w-44 h-44 bg-white p-3 rounded-2xl mx-auto flex flex-col items-center justify-center shadow-inner"> <Icons.QrCode className="w-36 h-36 text-slate-900" /> <span className="text-[8px] text-slate-600 font-black tracking-widest uppercase">Thai QR PromptPay</span> </div> <div className="text-xs space-y-0.5 text-slate-300"> <p>ยอดชำระ: <strong className="text-orange-400 text-lg font-black">฿{confirmedOrder.grandTotal.toLocaleString()}</strong></p> <p className="text-slate-400">ธนาคารกสิกรไทย: 012-3-45678-9 (บจก. เอเพ็กซ์ สตูดิโอ)</p> </div> </div> ) : ( <div className="glass-card p-5 rounded-2xl text-center space-y-2 border border-orange-500/30"> <p className="font-extrabold text-sm text-white">เก็บเงินปลายทาง (Cash on Delivery)</p> <p className="text-xs text-slate-300"> เตรียมเงินสดหรือสแกนจ่ายกับพนักงานขนส่ง Flash Express / Kerry ยอดรวม <strong>฿{confirmedOrder.grandTotal.toLocaleString()}</strong> </p> </div> )} <div className="flex gap-2"> <button onClick={() => { setConfirmedOrder(null); setMemberHubTab('orders'); setIsMemberHubOpen(true); }} className="flex-1 py-3 rounded-xl bg-slate-800 text-xs font-bold text-slate-200 cursor-pointer" > ดูคำสั่งซื้อใน Shopee VIP </button> <button onClick={() => setConfirmedOrder(null)} className="flex-1 py-3 rounded-xl shopee-gradient text-white font-black text-xs cursor-pointer" > กลับสู่หน้าร้าน </button> </div> </div> </div> )} {/* ALL MODALS */} <ShopeeMemberHubModal isOpen={isMemberHubOpen} onClose={() => setIsMemberHubOpen(false)} currentUser={currentUser} onLogout={handleLogout} userOrders={userOrders} coinLogs={coinLogs} initialTab={memberHubTab} onProfileUpdate={(u) => setCurrentUser(u)} onClaimCoins={handleClaimCoins} onSpinWheel={handleSpinWheel} onClaimMission={handleClaimMission} onCollectVoucher={handleCollectVoucher} onQuickSwitchTier={handleQuickSwitchTier} onUploadSlipForOrder={(orderId, slipUrl) => { setUserOrders(prev => prev.map(o => o.orderId === orderId ? { ...o, slipImage: slipUrl, status: 'Slip Uploaded' } : o)); }} /> <AddProductModal isOpen={isAddProductOpen} onClose={() => setIsAddProductOpen(false)} onProductAdded={(newCatalog) => setFashionCatalog(newCatalog)} /> <ProductDetailModal isOpen={!!selectedProductModal} onClose={() => setSelectedProductModal(null)} product={selectedProductModal} onAddToCart={handleAddToCart} /> <LoginModal isOpen={isLoginModalOpen} onClose={() => setIsLoginModalOpen(false)} onSuccess={(u, msg) => { setCurrentUser(u); addToast(msg); setIsLoginModalOpen(false); }} onSwitchToRegister={() => { setIsLoginModalOpen(false); setIsRegisterModalOpen(true); }} onQuickSwitch={handleQuickSwitchTier} /> <RegisterModal isOpen={isRegisterModalOpen} onClose={() => setIsRegisterModalOpen(false)} onSuccess={(u, msg) => { setCurrentUser(u); addToast(msg); setIsRegisterModalOpen(false); }} onSwitchToLogin={() => { setIsRegisterModalOpen(false); setIsLoginModalOpen(true); }} onQuickSwitch={handleQuickSwitchTier} /> {/* TOAST NOTIFICATIONS */} <div className="fixed bottom-5 right-5 z-50 space-y-2 pointer-events-none"> {toasts.map((toast) => ( <div key={toast.id} className="pointer-events-auto px-4 py-3 rounded-2xl bg-slate-900 text-white border border-orange-500/50 text-xs font-bold flex items-center space-x-2 shadow-2xl animate-fade-in"> <span className="text-orange-400">✨</span> <span>{toast.message}</span> </div> ))} </div> {/* FOOTER */} <footer className="mt-auto border-t border-slate-800 bg-[#060a14] py-8 px-4 sm:px-8 text-xs text-slate-500"> <div className="max-w-[1600px] mx-auto flex flex-col sm:flex-row items-center justify-between gap-4 text-center sm:text-left"> <div className="space-y-1"> <div className="flex items-center space-x-2 justify-center sm:justify-start"> <span className="font-bold text-slate-300">APEX STUDIO OFFICIAL SHOP</span> <span className="bg-rose-600 text-white text-[9px] font-black px-1.5 py-0.5 rounded">SHOPEE MALL</span> </div> <p>© 2026 APEX STUDIO. All rights reserved. Shopee Member Club & Fashion Store.</p> </div> <div className="flex items-center space-x-4 font-semibold text-slate-400"> <span>100% Authentic</span> <span>•</span> <span>Shopee Coins</span> <span>•</span> <span>COD Supported</span> </div> </div> </footer> </div> ); } ReactDOM.createRoot(document.getElementById('root')).render(<App />); </script> </body> </html>
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.22 |
proxy
|
phpinfo
|
Settings