| <x-app-layout> |
| <x-slot name="header"> |
| <div class="flex items-center justify-between"> |
| <div> |
| <h1 class="heading-2 mb-2">Product Management</h1> |
| <p class="text-muted">Manage your store inventory with precision and ease</p> |
| </div> |
| <button id="show_category_modal" class="btn-primary"> |
| <i class="fas fa-plus mr-2"></i>Add New Product |
| </button> |
| </div> |
| </x-slot> |
| |
| <div class="py-12"> |
| <div class="container-custom"> |
| @if (session('success')) |
| <div class="card mb-6 bg-green-900/30 border-green-500/30"> |
| <div class="flex items-center"> |
| <i class="fas fa-check-circle text-green-400 mr-3 text-xl"></i> |
| <span class="text-green-300 font-medium">{{ session('success') }}</span> |
| </div> |
| </div> |
| @endif |
|
|
| <!-- Product Inventory Card --> |
| <div class="card mb-8"> |
| <!-- Header Section --> |
| <div class="flex flex-col lg:flex-row lg:items-center justify-between mb-8 gap-6"> |
| <div class="flex items-center"> |
| <div class="w-12 h-12 bg-gradient-to-r from-blue-500 to-purple-500 rounded-xl flex items-center justify-center mr-4"> |
| <i class="fas fa-boxes text-white"></i> |
| </div> |
| <div> |
| <h2 class="heading-3 mb-1">Product Inventory</h2> |
| <p class="text-muted"> |
| @if(request()->hasAny(['game', 'stock', 'search'])) |
| {{ $products->count() }} filtered results |
| @if($products->count() > 0) |
| <span class="text-white/40">•</span> |
| <a href="{{ route('products.listOFproduct') }}" class="text-purple-400 hover:text-purple-300 text-sm"> |
| View all products |
| </a> |
| @endif |
| @else |
| {{ $products->count() }} products in your catalog |
| @endif |
| </p> |
| </div> |
| </div> |
| </div> |
| |
| <!-- Enhanced Search and Filter Section --> |
| <div class="mb-8"> |
| <form method="GET" action="{{ route('products.listOFproduct') }}" class="space-y-4"> |
| <!-- Search Bar - Full Width --> |
| <div class="relative"> |
| <input type="text" name="search" value="{{ request('search') }}" |
| placeholder="🔍 Search products by name or description..." |
| class="input-field pl-12 pr-4 w-full text-lg py-4 bg-black/40 border-2 border-white/20 focus:border-purple-500/60 rounded-xl"> |
| <i class="fas fa-search absolute left-4 top-1/2 transform -translate-y-1/2 text-white/60 text-lg"></i> |
| @if(request('search')) |
| <button type="button" onclick="clearSearch()" class="absolute right-4 top-1/2 transform -translate-y-1/2 text-white/60 hover:text-white"> |
| <i class="fas fa-times"></i> |
| </button> |
| @endif |
| </div> |
| |
| <!-- Filters Row --> |
| <div class="flex flex-col sm:flex-row items-center gap-4"> |
| <select name="stock" class="input-field flex-1 min-w-0"> |
| <option value="">📦 All Stock</option> |
| <option value="in_stock" {{ request('stock') == 'in_stock' ? 'selected' : '' }}> |
| ✅ In Stock |
| </option> |
| <option value="low_stock" {{ request('stock') == 'low_stock' ? 'selected' : '' }}> |
| ⚠️ Low Stock |
| </option> |
| <option value="out_of_stock" {{ request('stock') == 'out_of_stock' ? 'selected' : '' }}> |
| ❌ Out of Stock |
| </option> |
| </select> |
| |
| <select name="game" class="input-field flex-1 min-w-0"> |
| <option value="">🎮 All Categories</option> |
| <optgroup label="Main Categories"> |
| <option value="Genshin" {{ request('game') == 'Genshin' ? 'selected' : '' }}>⭐ Genshin Impact</option> |
| <option value="Starrail" {{ request('game') == 'Starrail' ? 'selected' : '' }}>🚀 Honkai: Star Rail</option> |
| <option value="WutheringWave" {{ request('game') == 'WutheringWave' ? 'selected' : '' }}>🌊 Wuthering Waves</option> |
| </optgroup> |
| <optgroup label="Additional Games"> |
| <option value="ZenlessZoneZero" {{ request('game') == 'ZenlessZoneZero' ? 'selected' : '' }}>🏙️ Zenless Zone Zero</option> |
| <option value="Arknights" {{ request('game') == 'Arknights' ? 'selected' : '' }}>♞ Arknights</option> |
| <option value="AzurLane" {{ request('game') == 'AzurLane' ? 'selected' : '' }}>🚢 Azur Lane</option> |
| </optgroup> |
| @if(isset($customGames) && $customGames->count() > 0) |
| <optgroup label="Custom Games"> |
| @foreach($customGames as $customGame) |
| <option value="{{ $customGame->name }}" {{ request('game') == $customGame->name ? 'selected' : '' }}> |
| 🎮 {{ $customGame->name }} |
| </option> |
| @endforeach |
| </optgroup> |
| @endif |
| </select> |
| |
| <div class="flex items-center gap-2 flex-shrink-0"> |
| <button type="submit" class="btn-primary px-6 py-3"> |
| <i class="fas fa-filter mr-2"></i>Apply Filters |
| </button> |
| <a href="{{ route('products.listOFproduct') }}" class="btn-secondary px-4 py-3"> |
| <i class="fas fa-refresh mr-1"></i>Clear |
| </a> |
| </div> |
| </div> |
| </form> |
| </div> |
| |
| @if($products->isEmpty()) |
| <div class="text-center py-16"> |
| <div class="w-20 h-20 bg-gradient-to-r from-gray-600 to-gray-800 rounded-full flex items-center justify-center mx-auto mb-6 opacity-50"> |
| <i class="fas fa-box-open text-3xl text-white"></i> |
| </div> |
| <h3 class="heading-3 mb-4">No Products Yet</h3> |
| <p class="text-muted mb-8">Start building your inventory by adding your first product.</p> |
| <button class="btn-primary" onclick="document.getElementById('show_category_modal').click()"> |
| <i class="fas fa-plus mr-2"></i>Add Your First Product |
| </button> |
| </div> |
| @else |
| <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-8"> |
| @foreach($products as $product) |
| <div class="product-card glass-bg border border-white/10 rounded-2xl overflow-hidden transition-all duration-500 hover:border-purple-500/50 hover:shadow-2xl hover:shadow-purple-500/20 group transform hover:scale-105"> |
| <!-- Product Image --> |
| <div class="relative h-64 overflow-hidden"> |
| <img src="{{ $product->image }}" alt="{{ $product->name }}" |
| class="w-full h-full object-cover transition-transform duration-500 group-hover:scale-110"> |
| |
| <!-- Gradient Overlay --> |
| <div class="absolute inset-0 bg-gradient-to-t from-black/90 via-black/30 to-transparent"></div> |
| |
| <!-- Stock Badge --> |
| <div class="absolute top-4 right-4"> |
| @if($product->Amount === 0) |
| <span class="px-4 py-2 rounded-xl text-sm font-semibold bg-red-500/90 text-white border border-red-400/50 shadow-lg backdrop-blur-sm"> |
| <i class="fas fa-times-circle mr-2"></i>Out of Stock |
| </span> |
| @elseif($product->Amount <= 5) |
| <span class="px-4 py-2 rounded-xl text-sm font-semibold bg-orange-500/90 text-white border border-orange-400/50 shadow-lg backdrop-blur-sm animate-pulse"> |
| <i class="fas fa-exclamation-triangle mr-2"></i>{{ $product->Amount }} Left |
| </span> |
| @else |
| <span class="px-4 py-2 rounded-xl text-sm font-semibold bg-green-500/90 text-white border border-green-400/50 shadow-lg backdrop-blur-sm"> |
| <i class="fas fa-check-circle mr-2"></i>In Stock |
| </span> |
| @endif |
| </div> |
| </div> |
| |
| <!-- Product Content --> |
| <div class="p-6"> |
| <!-- Price Display --> |
| <div class="mb-4"> |
| <div class="bg-black/90 backdrop-blur-sm rounded-xl px-5 py-3 border border-purple-500/30"> |
| <div class="text-xs text-white/60 mb-1">Price</div> |
| <div class="text-2xl font-bold text-white"> |
| ฿{{ number_format($product->price, 2) }} |
| </div> |
| </div> |
| </div> |
| |
| <!-- Game Category Badge --> |
| @if($product->game) |
| <div class="mb-4"> |
| <span class="px-3 py-2 rounded-full text-sm font-semibold |
| @if($product->game == 'Genshin') |
| bg-yellow-500/20 text-yellow-300 border border-yellow-500/30 |
| @elseif($product->game == 'Starrail') |
| bg-purple-500/20 text-purple-300 border border-purple-500/30 |
| @elseif($product->game == 'WutheringWave') |
| bg-cyan-500/20 text-cyan-300 border border-cyan-500/30 |
| @else |
| bg-blue-500/20 text-blue-300 border border-blue-500/30 |
| @endif"> |
| @if($product->game == 'Genshin') |
| <i class="fas fa-star mr-2"></i>Genshin Impact |
| @elseif($product->game == 'Starrail') |
| <i class="fas fa-rocket mr-2"></i>Honkai: Star Rail |
| @elseif($product->game == 'WutheringWave') |
| <i class="fas fa-wave-square mr-2"></i>Wuthering Waves |
| @else |
| <i class="fas fa-gamepad mr-2"></i>{{ $product->game }} |
| @endif |
| </span> |
| </div> |
| @endif |
| |
| <!-- Product Title --> |
| <h3 class="text-xl font-bold text-white mb-3 group-hover:text-purple-300 transition-colors duration-300"> |
| {{ $product->name }} |
| </h3> |
| |
| <!-- Product Description --> |
| <p class="text-white/70 text-sm mb-6 line-clamp-3 leading-relaxed"> |
| {{ $product->description }} |
| </p> |
| |
| <!-- Action Buttons --> |
| <div class="flex items-center space-x-3"> |
| <button class="flex-1 py-3 px-4 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-500 hover:to-purple-500 text-white rounded-xl font-semibold transition-all duration-300 shadow-lg hover:shadow-purple-500/30 transform hover:scale-105" onclick="editProduct({{ $product->id }}, '{{ addslashes($product->name) }}', '{{ addslashes($product->description) }}', {{ $product->price }}, {{ $product->Amount }}, '{{ $product->game }}', '{{ $product->image }}')"> |
| <i class="fas fa-edit mr-2"></i>Edit |
| </button> |
| <form action="{{ route('products.destroy', $product->id) }}" method="POST" class="inline"> |
| @csrf |
| @method('DELETE') |
| <button type="submit" class="py-3 px-4 bg-gradient-to-r from-red-600 to-red-700 hover:from-red-500 hover:to-red-600 text-white rounded-xl font-semibold transition-all duration-300 shadow-lg hover:shadow-red-500/30 transform hover:scale-105" |
| onclick="return confirm('Are you sure you want to delete this product?')"> |
| <i class="fas fa-trash mr-2"></i>Delete |
| </button> |
| </form> |
| </div> |
| </div> |
| </div> |
| @endforeach |
| </div> |
| @endif |
| </div> |
| </div> |
| </div> |
| |
| <!-- Add/Edit Product Modal --> |
| <div id="productModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm hidden items-center justify-center z-50 p-4"> |
| <div class="card max-w-2xl w-full max-h-[90vh] flex flex-col"> |
| <div class="flex-shrink-0 flex items-center justify-between mb-6"> |
| <h3 class="heading-3" id="modalTitle">Add New Product</h3> |
| <button onclick="closeModal()" class="text-white/60 hover:text-white text-2xl transition-colors"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| |
| <div class="flex-1 overflow-y-auto custom-scrollbar pr-2"> |
| <form id="productForm" method="POST" enctype="multipart/form-data" class="space-y-6"> |
| @csrf |
| <div id="methodField"></div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Product Name</label> |
| <input type="text" name="name" id="productName" required class="input-field"> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Description</label> |
| <textarea name="description" id="productDescription" rows="3" class="input-field resize-none"></textarea> |
| </div> |
| |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Price (฿)</label> |
| <input type="number" name="price" id="productPrice" step="0.01" required class="input-field"> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Stock Amount</label> |
| <input type="number" name="Amount" id="productAmount" required class="input-field"> |
| </div> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Game Category</label> |
| <select name="game" id="productGame" class="input-field"> |
| <option value="">Select Game</option> |
| <optgroup label="Main Categories"> |
| <option value="Genshin">Genshin Impact</option> |
| <option value="Starrail">Honkai: Star Rail</option> |
| <option value="WutheringWave">Wuthering Waves</option> |
| </optgroup> |
| <optgroup label="Additional Games"> |
| <option value="ZenlessZoneZero">Zenless Zone Zero</option> |
| <option value="Arknights">Arknights</option> |
| <option value="AzurLane">Azur Lane</option> |
| </optgroup> |
| @if(isset($customGames) && $customGames->count() > 0) |
| <optgroup label="Custom Games" id="existingCustomGames"> |
| @foreach($customGames as $customGame) |
| <option value="{{ $customGame->name }}">{{ $customGame->name }}</option> |
| @endforeach |
| </optgroup> |
| @endif |
| </select> |
| </div> |
| |
| <!-- Product Specifications Section --> |
| <div class="border-t border-white/10 pt-6"> |
| <h4 class="text-lg font-semibold text-white mb-4 flex items-center"> |
| <i class="fas fa-cog mr-2 text-purple-400"></i> |
| Product Specifications |
| </h4> |
| |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Product Type</label> |
| <select name="product_type" id="productType" class="input-field"> |
| <option value="Digital Account">Digital Account</option> |
| <option value="Game Currency">Game Currency</option> |
| <option value="Items">Items</option> |
| <option value="Boost Service">Boost Service</option> |
| </select> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Platform</label> |
| <select name="platform" id="productPlatform" class="input-field"> |
| <option value="Multi-Platform">Multi-Platform</option> |
| <option value="PC">PC</option> |
| <option value="Mobile">Mobile</option> |
| <option value="PlayStation">PlayStation</option> |
| <option value="Xbox">Xbox</option> |
| </select> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Region</label> |
| <select name="region" id="productRegion" class="input-field"> |
| <option value="Global">Global</option> |
| <option value="Asia">Asia</option> |
| <option value="Europe">Europe</option> |
| <option value="America">America</option> |
| <option value="China">China</option> |
| </select> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Delivery Method</label> |
| <select name="delivery_method" id="deliveryMethod" class="input-field"> |
| <option value="Instant Digital">Instant Digital</option> |
| <option value="Manual Delivery">Manual Delivery</option> |
| <option value="Email Delivery">Email Delivery</option> |
| </select> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Warranty Period</label> |
| <select name="warranty" id="productWarranty" class="input-field"> |
| <option value="Lifetime Support">Lifetime Support</option> |
| <option value="1 Year">1 Year</option> |
| <option value="6 Months">6 Months</option> |
| <option value="3 Months">3 Months</option> |
| <option value="1 Month">1 Month</option> |
| <option value="No Warranty">No Warranty</option> |
| </select> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Support Level</label> |
| <select name="support_level" id="supportLevel" class="input-field"> |
| <option value="24/7 Available">24/7 Available</option> |
| <option value="Business Hours">Business Hours</option> |
| <option value="Email Only">Email Only</option> |
| <option value="Limited Support">Limited Support</option> |
| </select> |
| </div> |
| </div> |
| |
| <div class="mt-4"> |
| <label class="block text-sm font-medium text-white mb-2">Additional Features</label> |
| <textarea name="features" id="productFeatures" rows="3" placeholder="List key features, benefits, or special notes about this product..." class="input-field resize-none"></textarea> |
| </div> |
| </div> |
| |
| <div> |
| <label class="block text-sm font-medium text-white mb-2">Product Image</label> |
| <div id="currentImagePreview" class="hidden mb-4"> |
| <div class="text-sm text-white/70 mb-2">Current Image:</div> |
| <img id="currentImage" src="" alt="Current product image" |
| class="w-32 h-32 object-cover rounded-lg border border-white/20"> |
| </div> |
| |
| <!-- Custom File Upload --> |
| <div class="relative"> |
| <div id="fileUploadArea" class="border-2 border-dashed border-white/20 rounded-xl p-6 text-center hover:border-purple-400/50 transition-all duration-300 cursor-pointer bg-black/20 hover:bg-black/30"> |
| <div id="uploadContent"> |
| <i class="fas fa-cloud-upload-alt text-3xl text-white/40 mb-3"></i> |
| <div class="text-white/80 mb-2"> |
| <span class="text-purple-400 hover:text-purple-300 font-medium">Choose file</span> |
| <span class="text-white/60"> or drag and drop</span> |
| </div> |
| <p class="text-xs text-white/50">PNG, JPG, GIF up to 10MB</p> |
| </div> |
| <div id="fileInfo" class="hidden"> |
| <i class="fas fa-file-image text-2xl text-green-400 mb-2"></i> |
| <div class="text-green-400 font-medium" id="fileName"></div> |
| <div class="text-white/60 text-sm" id="fileSize"></div> |
| <button type="button" id="removeFile" class="mt-2 text-red-400 hover:text-red-300 text-sm transition-colors"> |
| <i class="fas fa-times mr-1"></i>Remove |
| </button> |
| </div> |
| </div> |
| <input type="file" name="image" id="productImage" accept="image |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| .custom-scrollbar::-webkit-scrollbar { |
| width: 8px; |
| } |
| |
| .custom-scrollbar::-webkit-scrollbar-track { |
| background: rgba(255, 255, 255, 0.1); |
| border-radius: 4px; |
| } |
| |
| .custom-scrollbar::-webkit-scrollbar-thumb { |
| background: rgba(102, 126, 234, 0.6); |
| border-radius: 4px; |
| transition: background 0.3s ease; |
| } |
| |
| .custom-scrollbar::-webkit-scrollbar-thumb:hover { |
| background: rgba(102, 126, 234, 0.8); |
| } |
| |
| |
| .custom-scrollbar { |
| scrollbar-width: thin; |
| scrollbar-color: rgba(102, 126, 234, 0.6) rgba(255, 255, 255, 0.1); |
| } |
| |
| |
| |
| z-index: 99999 !important; |
| position: fixed !important; |
| top: auto !important; |
| right: auto !important; |
| } |
| |
| |
| .relative { |
| position: relative; |
| z-index: auto; |
| } |
| |
| |
| |
| position: fixed !important; |
| z-index: 99999 !important; |
| } |
| |
| |
| |
| transform: translateY(-10px); |
| opacity: 0; |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
| pointer-events: none; |
| } |
| |
| |
| transform: translateY(0); |
| opacity: 1; |
| pointer-events: auto; |
| } |
| |
| |
| |
| transition: all 0.3s ease; |
| } |
| |
| |
| transform: translateY(-2px); |
| box-shadow: 0 8px 25px rgba(102, 126, 234, 0.2); |
| } |
| |
| |
| border-color: rgba(102, 126, 234, 0.8) !important; |
| background: rgba(102, 126, 234, 0.1) !important; |
| transform: scale(1.02); |
| } |
|
|
| |
| .custom-game-card.delete-mode { |
| animation: gentleShake 2s ease-in-out infinite; |
| filter: brightness(0.8) saturate(0.7); |
| } |
|
|
| .custom-game-card.delete-mode .delete-btn { |
| opacity: 1 !important; |
| visibility: visible !important; |
| transform: scale(1) !important; |
| animation: pulseGlow 1.5s ease-in-out infinite; |
| } |
|
|
| @keyframes gentleShake { |
| 0%, 100% { transform: translateX(0); } |
| 25% { transform: translateX(-1px); } |
| 75% { transform: translateX(1px); } |
| } |
|
|
| @keyframes pulseGlow { |
| 0%, 100% { |
| box-shadow: 0 0 5px rgba(239, 68, 68, 0.5); |
| transform: scale(1); |
| } |
| 50% { |
| box-shadow: 0 0 15px rgba(239, 68, 68, 0.8); |
| transform: scale(1.05); |
| } |
| } |
|
|
| |
| .custom-game-card:not(.delete-mode) { |
| transition: all 0.3s ease; |
| } |
|
|
| |
| .custom-game-card button { |
| transition: all 0.2s ease; |
| } |
|
|
| .custom-game-card button:hover { |
| transform: scale(1.02); |
| } |
|
|
| |
| .custom-notification { |
| backdrop-filter: blur(10px); |
| border: 1px solid rgba(255, 255, 255, 0.1); |
| } |
|
|
| |
| .delete-mode-overlay { |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| bottom: 0; |
| background: rgba(0, 0, 0, 0.3); |
| z-index: 5; |
| pointer-events: none; |
| } |
| </style> |
|
|
| <script> |
| |
| function clearSearch() { |
| const searchInput = document.querySelector('input[name="search"]'); |
| searchInput.value = ''; |
| searchInput.form.submit(); |
| } |
|
|
| |
| document.querySelector('input[name="search"]').addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') { |
| e.preventDefault(); |
| this.form.submit(); |
| } |
| }); |
|
|
| |
| function openModal(title = 'Add New Product', action = '{{ route("products.store") }}', method = 'POST') { |
| document.getElementById('modalTitle').textContent = title; |
| document.getElementById('productForm').action = action; |
| document.getElementById('methodField').innerHTML = method === 'PUT' ? '@method("PUT")' : ''; |
| document.getElementById('productModal').classList.remove('hidden'); |
| document.getElementById('productModal').classList.add('flex'); |
| } |
| |
| function closeModal() { |
| document.getElementById('productModal').classList.add('hidden'); |
| document.getElementById('productModal').classList.remove('flex'); |
| document.getElementById('productForm').reset(); |
| |
| |
| document.getElementById('currentImagePreview').classList.add('hidden'); |
| |
| |
| document.getElementById('productImage').setAttribute('required', 'required'); |
| } |
| |
| function editProduct(id, name, description, price, amount, game, image, productType = 'Digital Account', platform = 'Multi-Platform', region = 'Global', deliveryMethod = 'Instant Digital', warranty = 'Lifetime Support', supportLevel = '24/7 Available', features = '') { |
| |
| document.getElementById('productName').value = name || ''; |
| document.getElementById('productDescription').value = description || ''; |
| document.getElementById('productPrice').value = price || ''; |
| document.getElementById('productAmount').value = amount || ''; |
| document.getElementById('productGame').value = game || ''; |
| |
| |
| document.getElementById('productType').value = productType || 'Digital Account'; |
| document.getElementById('productPlatform').value = platform || 'Multi-Platform'; |
| document.getElementById('productRegion').value = region || 'Global'; |
| document.getElementById('deliveryMethod').value = deliveryMethod || 'Instant Digital'; |
| document.getElementById('productWarranty').value = warranty || 'Lifetime Support'; |
| document.getElementById('supportLevel').value = supportLevel || '24/7 Available'; |
| document.getElementById('productFeatures').value = features || ''; |
| |
| |
| const currentImagePreview = document.getElementById('currentImagePreview'); |
| const currentImage = document.getElementById('currentImage'); |
| if (image) { |
| currentImage.src = image; |
| currentImagePreview.classList.remove('hidden'); |
| } else { |
| currentImagePreview.classList.add('hidden'); |
| } |
| |
| |
| document.getElementById('productImage').value = ''; |
| |
| |
| document.getElementById('productImage').removeAttribute('required'); |
| |
| openModal('Edit Product', `/products/${id}`, 'PUT'); |
| } |
| |
| |
| document.getElementById('show_category_modal').addEventListener('click', () => { |
| console.log('Category modal button clicked'); |
| const categoryModal = document.getElementById('categoryModal'); |
| if (categoryModal) { |
| categoryModal.classList.remove('hidden'); |
| categoryModal.classList.add('flex'); |
| console.log('Category modal shown'); |
| } else { |
| console.error('Category modal not found'); |
| } |
| }); |
| |
| |
| function closeCategoryModal() { |
| document.getElementById('categoryModal').classList.add('hidden'); |
| document.getElementById('categoryModal').classList.remove('flex'); |
| } |
| |
| |
| document.getElementById('categoryModal').addEventListener('click', (e) => { |
| if (e.target === e.currentTarget) { |
| closeCategoryModal(); |
| } |
| }); |
| |
| |
| function showOtherGamesModal() { |
| console.log('showOtherGamesModal called'); |
| closeCategoryModal(); |
| const otherGamesModal = document.getElementById('otherGamesModal'); |
| if (otherGamesModal) { |
| otherGamesModal.classList.remove('hidden'); |
| otherGamesModal.classList.add('flex'); |
| console.log('Other games modal shown'); |
| } else { |
| console.error('Other games modal not found'); |
| } |
| } |
| |
| |
| function closeOtherGamesModal() { |
| document.getElementById('otherGamesModal').classList.add('hidden'); |
| document.getElementById('otherGamesModal').classList.remove('flex'); |
| } |
| |
| |
| function backToCategoryModal() { |
| closeOtherGamesModal(); |
| document.getElementById('categoryModal').classList.remove('hidden'); |
| document.getElementById('categoryModal').classList.add('flex'); |
| } |
| |
| |
| document.getElementById('otherGamesModal').addEventListener('click', (e) => { |
| if (e.target === e.currentTarget) { |
| closeOtherGamesModal(); |
| } |
| }); |
| |
| |
| function showCustomGameInput() { |
| console.log('showCustomGameInput called'); |
| const customGameInput = document.getElementById('customGameInput'); |
| const customGameName = document.getElementById('customGameName'); |
| |
| if (!customGameInput) { |
| console.error('customGameInput element not found'); |
| alert('Error: Custom game input section not found. Please refresh the page and try again.'); |
| return; |
| } |
| |
| if (!customGameName) { |
| console.error('customGameName element not found'); |
| alert('Error: Custom game name input not found. Please refresh the page and try again.'); |
| return; |
| } |
| |
| |
| customGameInput.classList.remove('hidden'); |
| |
| |
| customGameName.value = ''; |
| |
| |
| setTimeout(() => { |
| customGameName.focus(); |
| customGameName.select(); |
| }, 150); |
| |
| console.log('Custom game input shown and focused'); |
| } |
| |
| |
| function hideCustomGameInput() { |
| console.log('hideCustomGameInput called'); |
| const customGameInput = document.getElementById('customGameInput'); |
| const customGameName = document.getElementById('customGameName'); |
| |
| if (customGameInput) { |
| customGameInput.classList.add('hidden'); |
| console.log('Custom game input hidden'); |
| } |
| |
| if (customGameName) { |
| customGameName.value = ''; |
| console.log('Custom game name input cleared'); |
| } |
| } |
|
|
| |
| function showDeleteCustomGameInput() { |
| console.log('showDeleteCustomGameInput called'); |
| |
| |
| const customGameCards = document.querySelectorAll('.custom-game-card'); |
| customGameCards.forEach(card => { |
| card.classList.add('delete-mode'); |
| }); |
| |
| |
| showDeleteModeMessage(); |
| |
| console.log('Delete mode activated for', customGameCards.length, 'custom games'); |
| } |
|
|
| |
| function hideDeleteCustomGameInput() { |
| console.log('hideDeleteCustomGameInput called'); |
| |
| |
| const customGameCards = document.querySelectorAll('.custom-game-card'); |
| customGameCards.forEach(card => { |
| card.classList.remove('delete-mode'); |
| }); |
| |
| |
| hideDeleteModeMessage(); |
| |
| console.log('Delete mode deactivated'); |
| } |
|
|
| |
| function showDeleteModeMessage() { |
| |
| let overlay = document.getElementById('deleteModeOverlay'); |
| if (!overlay) { |
| overlay = document.createElement('div'); |
| overlay.id = 'deleteModeOverlay'; |
| overlay.className = 'fixed top-0 left-0 right-0 bg-red-500/90 text-white text-center py-3 z-50 transform -translate-y-full transition-transform duration-300'; |
| overlay.innerHTML = ` |
| <div class="flex items-center justify-center space-x-4"> |
| <i class="fas fa-trash text-xl"></i> |
| <span class="font-semibold">Delete Mode Active - Click minus buttons to delete custom games</span> |
| <button onclick="hideDeleteCustomGameInput()" class="bg-white/20 hover:bg-white/30 px-4 py-1 rounded-full text-sm transition-colors"> |
| <i class="fas fa-times mr-1"></i>Cancel |
| </button> |
| </div> |
| `; |
| document.body.appendChild(overlay); |
| } |
| |
| // Show overlay |
| setTimeout(() => { |
| overlay.classList.remove('-translate-y-full'); |
| overlay.classList.add('translate-y-0'); |
| }, 100); |
| } |
|
|
| |
| function hideDeleteModeMessage() { |
| const overlay = document.getElementById('deleteModeOverlay'); |
| if (overlay) { |
| overlay.classList.remove('translate-y-0'); |
| overlay.classList.add('-translate-y-full'); |
| } |
| } |
|
|
| |
| async function deleteCustomGame(gameName) { |
| if (confirm(`Are you sure you want to delete the custom game "${gameName}"?\n\nThis will remove it from all dropdowns. If any products are using this category, you'll need to reassign them first.`)) { |
| console.log('Deleting custom game:', gameName); |
| |
| try { |
| // Show loading state |
| const deleteBtn = event.target.closest('.delete-btn'); |
| if (deleteBtn) { |
| deleteBtn.innerHTML = '<i class="fas fa-spinner fa-spin text-sm"></i>'; |
| deleteBtn.disabled = true; |
| } |
| |
| // Call backend API |
| const response = await fetch(`/custom-games/${encodeURIComponent(gameName)}`, { |
| method: 'DELETE', |
| headers: { |
| 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '', |
| 'Content-Type': 'application/json', |
| 'Accept': 'application/json' |
| } |
| }); |
| |
| const result = await response.json(); |
| |
| if (result.success) { |
| // Remove from product game select dropdown |
| const gameSelect = document.getElementById('productGame'); |
| if (gameSelect) { |
| const optionToRemove = Array.from(gameSelect.options).find(option => option.value === gameName); |
| if (optionToRemove) { |
| optionToRemove.remove(); |
| console.log('Removed from product select:', gameName); |
| } |
| |
| // Check if Custom Games optgroup is empty and remove it |
| const customOptgroup = gameSelect.querySelector('optgroup[label="Custom Games"]'); |
| if (customOptgroup && customOptgroup.children.length === 0) { |
| customOptgroup.remove(); |
| console.log('Removed empty Custom Games optgroup'); |
| } |
| } |
| |
| // Remove from localStorage |
| localStorage.removeItem(`customGame_${gameName}`); |
| |
| // Remove the card from the modal with animation |
| const customGameCards = document.querySelectorAll('.custom-game-card'); |
| customGameCards.forEach(card => { |
| const button = card.querySelector('button[onclick*="' + gameName + '"]'); |
| if (button) { |
| // Add fade out animation |
| card.style.transition = 'all 0.3s ease'; |
| card.style.transform = 'scale(0.8)'; |
| card.style.opacity = '0'; |
| |
| setTimeout(() => { |
| card.remove(); |
| console.log('Removed card for:', gameName); |
| |
| // Check if there are any custom games left |
| const remainingCards = document.querySelectorAll('.custom-game-card'); |
| if (remainingCards.length === 0) { |
| hideDeleteCustomGameInput(); |
| } |
| }, 300); |
| } |
| }); |
| |
| // Show success message with better styling |
| showNotification(result.message, 'success'); |
| |
| } else { |
| // Show error message |
| showNotification(result.message, 'error'); |
| |
| // Reset delete button |
| if (deleteBtn) { |
| deleteBtn.innerHTML = '<i class="fas fa-minus text-sm"></i>'; |
| deleteBtn.disabled = false; |
| } |
| } |
| |
| } catch (error) { |
| console.error('Error deleting custom game:', error); |
| showNotification('Failed to delete custom game. Please try again.', 'error'); |
| |
| // Reset delete button |
| const deleteBtn = event.target.closest('.delete-btn'); |
| if (deleteBtn) { |
| deleteBtn.innerHTML = '<i class="fas fa-minus text-sm"></i>'; |
| deleteBtn.disabled = false; |
| } |
| } |
| } |
| } |
| |
| // Show notification function |
| function showNotification(message, type = 'info') { |
| // Remove existing notifications |
| const existingNotifications = document.querySelectorAll('.custom-notification'); |
| existingNotifications.forEach(notification => notification.remove()); |
| |
| // Create notification element |
| const notification = document.createElement('div'); |
| notification.className = `custom-notification fixed top-4 right-4 z-50 p-4 rounded-lg shadow-lg transform translate-x-full transition-all duration-300 ${ |
| type === 'success' ? 'bg-green-500/90 text-white' : |
| type === 'error' ? 'bg-red-500/90 text-white' : |
| 'bg-blue-500/90 text-white' |
| }`; |
| |
| notification.innerHTML = ` |
| <div class="flex items-center space-x-3"> |
| <i class="fas ${ |
| type === 'success' ? 'fa-check-circle' : |
| type === 'error' ? 'fa-exclamation-circle' : |
| 'fa-info-circle' |
| }"></i> |
| <span>${message}</span> |
| <button onclick="this.parentElement.parentElement.remove()" class="ml-2 hover:bg-white/20 rounded p-1"> |
| <i class="fas fa-times"></i> |
| </button> |
| </div> |
| `; |
| |
| document.body.appendChild(notification); |
| |
| // Animate in |
| setTimeout(() => { |
| notification.classList.remove('translate-x-full'); |
| notification.classList.add('translate-x-0'); |
| }, 100); |
| |
| // Auto remove after 5 seconds |
| setTimeout(() => { |
| notification.classList.add('translate-x-full'); |
| setTimeout(() => notification.remove(), 300); |
| }, 5000); |
| } |
| |
| // Quick custom game functions (same as main custom game functions) |
| function showQuickCustomGameInput() { |
| console.log('showQuickCustomGameInput called'); |
| const quickCustomGameInput = document.getElementById('quickCustomGameInput'); |
| const quickCustomGameName = document.getElementById('quickCustomGameName'); |
| |
| if (!quickCustomGameInput) { |
| console.error('quickCustomGameInput element not found'); |
| alert('Error: Quick custom game input section not found. Please refresh the page and try again.'); |
| return; |
| } |
| |
| if (!quickCustomGameName) { |
| console.error('quickCustomGameName element not found'); |
| alert('Error: Quick custom game name input not found. Please refresh the page and try again.'); |
| return; |
| } |
| |
| // Show the input section |
| quickCustomGameInput.classList.remove('hidden'); |
| |
| // Clear any previous input |
| quickCustomGameName.value = ''; |
| |
| // Focus on the input with a small delay to ensure visibility |
| setTimeout(() => { |
| quickCustomGameName.focus(); |
| quickCustomGameName.select(); |
| }, 150); |
| |
| console.log('Quick custom game input shown and focused'); |
| } |
| |
| function hideQuickCustomGameInput() { |
| console.log('hideQuickCustomGameInput called'); |
| const quickCustomGameInput = document.getElementById('quickCustomGameInput'); |
| const quickCustomGameName = document.getElementById('quickCustomGameName'); |
| |
| if (quickCustomGameInput) { |
| quickCustomGameInput.classList.add('hidden'); |
| console.log('Quick custom game input hidden'); |
| } |
| |
| if (quickCustomGameName) { |
| quickCustomGameName.value = ''; |
| console.log('Quick custom game name input cleared'); |
| } |
| } |
| |
| async function createQuickCustomGame() { |
| console.log('createQuickCustomGame called'); |
| const quickCustomGameNameInput = document.getElementById('quickCustomGameName'); |
| |
| if (!quickCustomGameNameInput) { |
| console.error('Quick custom game name input not found'); |
| alert('Error: Quick custom game input field not found. Please refresh the page and try again.'); |
| return; |
| } |
| |
| const customGameName = quickCustomGameNameInput.value.trim(); |
| console.log('Quick custom game name:', customGameName); |
| |
| if (!customGameName) { |
| alert('Please enter a game name'); |
| quickCustomGameNameInput.focus(); |
| return; |
| } |
| |
| // Validate game name length |
| if (customGameName.length < 2) { |
| alert('Game name must be at least 2 characters long'); |
| quickCustomGameNameInput.focus(); |
| return; |
| } |
| |
| if (customGameName.length > 50) { |
| alert('Game name must be less than 50 characters'); |
| quickCustomGameNameInput.focus(); |
| return; |
| } |
| |
| try { |
| // Generate random styling |
| const gameStyle = getRandomGameStyle(customGameName); |
| |
| // Call backend API to store custom game |
| const response = await fetch('/custom-games', { |
| method: 'POST', |
| headers: { |
| 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '', |
| 'Content-Type': 'application/json', |
| 'Accept': 'application/json' |
| }, |
| body: JSON.stringify({ |
| name: customGameName, |
| icon: gameStyle.icon, |
| color_gradient: gameStyle.gradient |
| }) |
| }); |
| |
| const result = await response.json(); |
| |
| if (result.success) { |
| // Add the custom game to the select dropdown |
| const gameSelect = document.getElementById('productGame'); |
| if (gameSelect) { |
| let customOptgroup = gameSelect.querySelector('optgroup[label="Custom Games"]'); |
| |
| // Create optgroup if it doesn't exist |
| if (!customOptgroup) { |
| customOptgroup = document.createElement('optgroup'); |
| customOptgroup.label = 'Custom Games'; |
| customOptgroup.id = 'customGamesOptgroup'; |
| gameSelect.appendChild(customOptgroup); |
| console.log('Created new Custom Games optgroup'); |
| } |
| |
| |
| const newOption = document.createElement('option'); |
| newOption.value = customGameName; |
| newOption.textContent = customGameName; |
| customOptgroup.appendChild(newOption); |
| console.log('Added new quick custom game option:', customGameName); |
| } |
| |
| |
| addCustomGameCardToModal(result.data); |
| |
| |
| hideQuickCustomGameInput(); |
| |
| |
| showCustomGameSuccessPopup(customGameName); |
| |
| |
| showNotification(result.message, 'success'); |
| } else { |
| showNotification(result.message, 'error'); |
| quickCustomGameNameInput.focus(); |
| } |
| } catch (error) { |
| console.error('Error creating custom game:', error); |
| showNotification('Failed to create custom game. Please try again.', 'error'); |
| quickCustomGameNameInput.focus(); |
| } |
| } |
| |
| |
| const customGameColors = [ |
| 'from-red-500 to-pink-500', |
| 'from-orange-500 to-red-500', |
| 'from-yellow-500 to-orange-500', |
| 'from-green-500 to-teal-500', |
| 'from-teal-500 to-cyan-500', |
| 'from-blue-500 to-indigo-500', |
| 'from-indigo-500 to-purple-500', |
| 'from-purple-500 to-pink-500', |
| 'from-pink-500 to-rose-500', |
| 'from-emerald-500 to-green-500', |
| 'from-cyan-500 to-blue-500', |
| 'from-violet-500 to-purple-500' |
| ]; |
| |
| const customGameIcons = [ |
| 'fas fa-gamepad', |
| 'fas fa-dice', |
| 'fas fa-chess', |
| 'fas fa-puzzle-piece', |
| 'fas fa-trophy', |
| 'fas fa-crown', |
| 'fas fa-gem', |
| 'fas fa-fire', |
| 'fas fa-bolt', |
| 'fas fa-magic', |
| 'fas fa-dragon', |
| 'fas fa-shield', |
| 'fas fa-sword', |
| 'fas fa-heart', |
| 'fas fa-star', |
| 'fas fa-moon', |
| 'fas fa-sun', |
| 'fas fa-leaf', |
| 'fas fa-snowflake', |
| 'fas fa-mountain' |
| ]; |
| |
| |
| function getRandomGameStyle(gameName) { |
| |
| let hash = 0; |
| for (let i = 0; i < gameName.length; i++) { |
| hash = ((hash << 5) - hash + gameName.charCodeAt(i)) & 0xffffffff; |
| } |
| |
| const colorIndex = Math.abs(hash) % customGameColors.length; |
| const iconIndex = Math.abs(hash >> 8) % customGameIcons.length; |
| |
| return { |
| gradient: customGameColors[colorIndex], |
| icon: customGameIcons[iconIndex] |
| }; |
| } |
| |
| |
| async function createCustomGame() { |
| console.log('createCustomGame called'); |
| const customGameNameInput = document.getElementById('customGameName'); |
| |
| if (!customGameNameInput) { |
| console.error('Custom game name input not found'); |
| alert('Error: Custom game input field not found. Please refresh the page and try again.'); |
| return; |
| } |
| |
| const customGameName = customGameNameInput.value.trim(); |
| console.log('Custom game name:', customGameName); |
| |
| if (!customGameName) { |
| alert('Please enter a game name'); |
| customGameNameInput.focus(); |
| return; |
| } |
| |
| |
| if (customGameName.length < 2) { |
| alert('Game name must be at least 2 characters long'); |
| customGameNameInput.focus(); |
| return; |
| } |
| |
| if (customGameName.length > 50) { |
| alert('Game name must be less than 50 characters'); |
| customGameNameInput.focus(); |
| return; |
| } |
|
|
| try { |
| |
| const gameStyle = getRandomGameStyle(customGameName); |
| |
| |
| const response = await fetch('/custom-games', { |
| method: 'POST', |
| headers: { |
| 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '', |
| 'Content-Type': 'application/json', |
| 'Accept': 'application/json' |
| }, |
| body: JSON.stringify({ |
| name: customGameName, |
| icon: gameStyle.icon, |
| color_gradient: gameStyle.gradient |
| }) |
| }); |
|
|
| const result = await response.json(); |
|
|
| if (result.success) { |
| |
| const gameSelect = document.getElementById('productGame'); |
| if (gameSelect) { |
| let customOptgroup = gameSelect.querySelector('optgroup[label="Custom Games"]'); |
| |
| |
| if (!customOptgroup) { |
| customOptgroup = document.createElement('optgroup'); |
| customOptgroup.label = 'Custom Games'; |
| customOptgroup.id = 'customGamesOptgroup'; |
| gameSelect.appendChild(customOptgroup); |
| console.log('Created new Custom Games optgroup'); |
| } |
| |
| |
| const newOption = document.createElement('option'); |
| newOption.value = customGameName; |
| newOption.textContent = customGameName; |
| customOptgroup.appendChild(newOption); |
| console.log('Added new custom game option:', customGameName); |
| } |
| |
| |
| addCustomGameCardToModal(result.data); |
| |
| |
| hideCustomGameInput(); |
| |
| |
| showCustomGameSuccessPopup(customGameName); |
| |
| |
| showNotification(result.message, 'success'); |
| } else { |
| showNotification(result.message, 'error'); |
| customGameNameInput.focus(); |
| } |
| } catch (error) { |
| console.error('Error creating custom game:', error); |
| showNotification('Failed to create custom game. Please try again.', 'error'); |
| customGameNameInput.focus(); |
| } |
| } |
|
|
| |
| function addCustomGameCardToModal(customGameData) { |
| const otherGamesModal = document.getElementById('otherGamesModal'); |
| if (!otherGamesModal) return; |
| |
| const gameGrid = otherGamesModal.querySelector('.grid'); |
| if (!gameGrid) return; |
| |
| |
| const gradientParts = customGameData.color_gradient.split(' '); |
| const primaryColor = gradientParts[0].replace('from-', '').replace('-500', ''); |
| |
| |
| const colorMap = { |
| 'red': ['from-red-500/20 to-pink-500/20', 'border-red-500/30', 'hover:border-red-500/50'], |
| 'pink': ['from-pink-500/20 to-rose-500/20', 'border-pink-500/30', 'hover:border-pink-500/50'], |
| 'rose': ['from-rose-500/20 to-pink-500/20', 'border-rose-500/30', 'hover:border-rose-500/50'], |
| 'purple': ['from-purple-500/20 to-blue-500/20', 'border-purple-500/30', 'hover:border-purple-500/50'], |
| 'blue': ['from-blue-500/20 to-indigo-500/20', 'border-blue-500/30', 'hover:border-blue-500/50'], |
| 'green': ['from-green-500/20 to-teal-500/20', 'border-green-500/30', 'hover:border-green-500/50'], |
| 'orange': ['from-orange-500/20 to-red-500/20', 'border-orange-500/30', 'hover:border-orange-500/50'], |
| 'yellow': ['from-yellow-500/20 to-orange-500/20', 'border-yellow-500/30', 'hover:border-yellow-500/50'], |
| 'teal': ['from-teal-500/20 to-cyan-500/20', 'border-teal-500/30', 'hover:border-teal-500/50'], |
| 'cyan': ['from-cyan-500/20 to-blue-500/20', 'border-cyan-500/30', 'hover:border-cyan-500/50'], |
| 'indigo': ['from-indigo-500/20 to-purple-500/20', 'border-indigo-500/30', 'hover:border-indigo-500/50'], |
| 'violet': ['from-violet-500/20 to-purple-500/20', 'border-violet-500/30', 'hover:border-violet-500/50'], |
| 'emerald': ['from-emerald-500/20 to-green-500/20', 'border-emerald-500/30', 'hover:border-emerald-500/50'] |
| }; |
| |
| const colors = colorMap[primaryColor] || colorMap['purple']; |
| |
| |
| const newCard = document.createElement('div'); |
| newCard.className = 'relative custom-game-card'; |
| newCard.innerHTML = ` |
| <button onclick="openNewProductModal('${customGameData.name}')" class="w-full p-6 bg-gradient-to-r ${colors[0]} border ${colors[1]} rounded-xl text-left ${colors[2]} transition-all duration-300 group"> |
| <div class="flex flex-col items-center text-center"> |
| <div class="w-12 h-12 bg-gradient-to-r ${customGameData.color_gradient} rounded-lg flex items-center justify-center mb-3 group-hover:scale-110 transition-transform duration-300"> |
| <i class="${customGameData.icon} text-white text-xl"></i> |
| </div> |
| <div class="text-white font-bold text-lg mb-1">${customGameData.name}</div> |
| <div class="${colors[0].includes('purple') ? 'text-purple-300' : 'text-' + primaryColor + '-300'} text-sm">Custom game</div> |
| </div> |
| </button> |
| <button onclick="deleteCustomGame('${customGameData.name}')" class="delete-btn absolute -top-2 -right-2 w-8 h-8 bg-red-500 hover:bg-red-600 text-white rounded-full flex items-center justify-center opacity-0 invisible transition-all duration-300 transform scale-0 z-10 shadow-lg"> |
| <i class="fas fa-minus text-sm"></i> |
| </button> |
| `; |
| |
| // Find the delete button (last item) and insert before it |
| const deleteButton = gameGrid.querySelector('button[onclick*="showDeleteCustomGameInput"]'); |
| if (deleteButton && deleteButton.parentElement) { |
| gameGrid.insertBefore(newCard, deleteButton.parentElement); |
| } else { |
| gameGrid.appendChild(newCard); |
| } |
| |
| // Add animation |
| newCard.style.opacity = '0'; |
| newCard.style.transform = 'scale(0.8)'; |
| setTimeout(() => { |
| newCard.style.transition = 'all 0.3s ease'; |
| newCard.style.opacity = '1'; |
| newCard.style.transform = 'scale(1)'; |
| }, 100); |
| } |
| |
| // Show custom game success popup |
| function showCustomGameSuccessPopup(gameName) { |
| console.log('showCustomGameSuccessPopup called with:', gameName); // Debug log |
| |
| const successNameElement = document.getElementById('customGameSuccessName'); |
| const gameNameButtonElement = document.getElementById('gameNameInButton'); |
| const successModal = document.getElementById('customGameSuccessModal'); |
| const addItemButton = document.getElementById('addItemForGame'); |
| |
| if (successNameElement && gameNameButtonElement && successModal && addItemButton) { |
| successNameElement.textContent = gameName; |
| gameNameButtonElement.textContent = gameName; |
| |
| // Set up the "Add Item" button click handler |
| addItemButton.onclick = function() { |
| closeCustomGameSuccessModal(); |
| openNewProductModal(gameName); |
| }; |
| |
| successModal.classList.remove('hidden'); |
| successModal.classList.add('flex'); |
| console.log('Success popup shown for game:', gameName); // Debug log |
| } else { |
| console.error('Success popup elements not found:'); // Debug log |
| console.log('successNameElement:', successNameElement); |
| console.log('gameNameButtonElement:', gameNameButtonElement); |
| console.log('successModal:', successModal); |
| console.log('addItemButton:', addItemButton); |
| |
| // Fallback alert if modal elements are missing |
| alert(`Custom game "${gameName}" has been added successfully!`); |
| } |
| } |
| |
| // Close custom game success modal |
| function closeCustomGameSuccessModal() { |
| document.getElementById('customGameSuccessModal').classList.add('hidden'); |
| document.getElementById('customGameSuccessModal').classList.remove('flex'); |
| } |
| |
| // Show add more category (go back to category modal) |
| function showAddMoreCategory() { |
| closeCustomGameSuccessModal(); |
| document.getElementById('categoryModal').classList.remove('hidden'); |
| document.getElementById('categoryModal').classList.add('flex'); |
| } |
| |
| // Handle Enter key in custom game input |
| document.addEventListener('DOMContentLoaded', function() { |
| const customGameInput = document.getElementById('customGameName'); |
| if (customGameInput) { |
| customGameInput.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') { |
| createCustomGame(); |
| } |
| }); |
| } |
| }); |
| |
| // Open new product modal with pre-selected category |
| function openNewProductModal(gameCategory) { |
| // Close both modals |
| closeCategoryModal(); |
| closeOtherGamesModal(); |
| |
| // Reset form for new product |
| document.getElementById('productForm').reset(); |
| document.getElementById('currentImagePreview').classList.add('hidden'); |
| document.getElementById('productImage').setAttribute('required', 'required'); |
| |
| // Set the selected game category |
| document.getElementById('productGame').value = gameCategory; |
| |
| // Set default values for specifications |
| document.getElementById('productType').value = 'Digital Account'; |
| document.getElementById('productPlatform').value = 'Multi-Platform'; |
| document.getElementById('productRegion').value = 'Global'; |
| document.getElementById('deliveryMethod').value = 'Instant Digital'; |
| document.getElementById('productWarranty').value = 'Lifetime Support'; |
| document.getElementById('supportLevel').value = '24/7 Available'; |
| |
| // Update modal title based on category |
| let modalTitle = 'Add New Product'; |
| if (gameCategory === 'Genshin') { |
| modalTitle = 'Add Genshin Impact Product'; |
| } else if (gameCategory === 'Starrail') { |
| modalTitle = 'Add Honkai: Star Rail Product'; |
| } else if (gameCategory === 'WutheringWave') { |
| modalTitle = 'Add Wuthering Waves Product'; |
| } else if (gameCategory === 'ZenlessZoneZero') { |
| modalTitle = 'Add Zenless Zone Zero Product'; |
| } else if (gameCategory === 'Arknights') { |
| modalTitle = 'Add Arknights Product'; |
| } else if (gameCategory === 'AzurLane') { |
| modalTitle = 'Add Azur Lane Product'; |
| } else { |
| modalTitle = `Add ${gameCategory} Product`; |
| } |
| |
| openModal(modalTitle); |
| } |
| |
| // Close modal when clicking outside |
| document.getElementById('productModal').addEventListener('click', (e) => { |
| if (e.target === e.currentTarget) { |
| closeModal(); |
| } |
| }); |
| |
| // Close success modal when clicking outside |
| document.getElementById('customGameSuccessModal').addEventListener('click', (e) => { |
| if (e.target === e.currentTarget) { |
| closeCustomGameSuccessModal(); |
| } |
| }); |
| |
| // File upload functionality |
| document.addEventListener('DOMContentLoaded', function() { |
| const fileInput = document.getElementById('productImage'); |
| const uploadArea = document.getElementById('fileUploadArea'); |
| const uploadContent = document.getElementById('uploadContent'); |
| const fileInfo = document.getElementById('fileInfo'); |
| const fileName = document.getElementById('fileName'); |
| const fileSize = document.getElementById('fileSize'); |
| const removeBtn = document.getElementById('removeFile'); |
| |
| // Handle file input change |
| fileInput.addEventListener('change', function(e) { |
| const file = e.target.files[0]; |
| if (file) { |
| handleFile(file); |
| } else { |
| resetFileDisplay(); |
| } |
| }); |
| |
| // Handle drag and drop |
| uploadArea.addEventListener('dragover', function(e) { |
| e.preventDefault(); |
| uploadArea.classList.add('dragover'); |
| }); |
| |
| uploadArea.addEventListener('dragleave', function(e) { |
| e.preventDefault(); |
| uploadArea.classList.remove('dragover'); |
| }); |
| |
| uploadArea.addEventListener('drop', function(e) { |
| e.preventDefault(); |
| uploadArea.classList.remove('dragover'); |
| |
| const files = e.dataTransfer.files; |
| if (files.length > 0) { |
| const file = files[0]; |
| if (file.type.startsWith('image/')) { |
| fileInput.files = files; |
| handleFile(file); |
| } |
| } |
| }); |
| |
| // Handle remove file |
| removeBtn.addEventListener('click', function(e) { |
| e.stopPropagation(); |
| fileInput.value = ''; |
| resetFileDisplay(); |
| }); |
| |
| function handleFile(file) { |
| // Validate file size (10MB) |
| if (file.size > 10 * 1024 * 1024) { |
| alert('File size must be less than 10MB'); |
| fileInput.value = ''; |
| return; |
| } |
| |
| // Validate file type |
| if (!file.type.startsWith('image/')) { |
| alert('Please select an image file'); |
| fileInput.value = ''; |
| return; |
| } |
| |
| // Show file info |
| fileName.textContent = file.name; |
| fileSize.textContent = formatFileSize(file.size); |
| uploadContent.classList.add('hidden'); |
| fileInfo.classList.remove('hidden'); |
| } |
| |
| function resetFileDisplay() { |
| uploadContent.classList.remove('hidden'); |
| fileInfo.classList.add('hidden'); |
| fileName.textContent = ''; |
| fileSize.textContent = ''; |
| } |
| |
| function formatFileSize(bytes) { |
| if (bytes === 0) return '0 Bytes'; |
| const k = 1024; |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; |
| } |
| }); |
| </script> |
| </x-app-layout> |