|
|
<?php |
|
|
|
|
|
namespace App\Http\Controllers; |
|
|
use App\Models\Product; |
|
|
use App\Models\CustomGame; |
|
|
use Illuminate\Http\Request; |
|
|
use Illuminate\Support\Facades\Storage; |
|
|
|
|
|
class ProductController extends Controller |
|
|
{ |
|
|
|
|
|
public function store(Request $request) |
|
|
{ |
|
|
$request->validate([ |
|
|
'name' => 'required', |
|
|
'price' => 'required|numeric', |
|
|
'description' => 'required', |
|
|
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', |
|
|
'Amount' => 'required|numeric', |
|
|
'game' => 'nullable|string' |
|
|
]); |
|
|
|
|
|
$product = new Product($request->only(['name', 'price', 'description', 'Amount', 'game'])); |
|
|
|
|
|
if ($request->hasFile('image')) { |
|
|
$imagePath = $request->file('image')->store('products', 'public'); |
|
|
$product->image = $imagePath; |
|
|
} |
|
|
|
|
|
$product->save(); |
|
|
|
|
|
return redirect()->back()->with('success', 'Product created successfully'); |
|
|
} |
|
|
public function show($category, $product_name) |
|
|
{ |
|
|
$product = Product::where('game', $category) |
|
|
->where('slug', $product_name) |
|
|
->firstOrFail(); |
|
|
return view('show', compact('product')); |
|
|
} |
|
|
|
|
|
public function categoryProducts($category) |
|
|
{ |
|
|
$products = Product::where('game', $category)->get(); |
|
|
|
|
|
|
|
|
$customGames = CustomGame::orderBy('name')->get(); |
|
|
|
|
|
return view('category_products', compact('products', 'category', 'customGames')); |
|
|
} |
|
|
|
|
|
public function edit($id) |
|
|
{ |
|
|
$product = Product::findOrFail($id); |
|
|
return response()->json($product); |
|
|
} |
|
|
public function listOFproduct(Request $request) |
|
|
{ |
|
|
$query = Product::query(); |
|
|
|
|
|
|
|
|
if ($request->filled('game')) { |
|
|
$query->where('game', $request->input('game')); |
|
|
} |
|
|
|
|
|
|
|
|
if ($request->filled('stock')) { |
|
|
$stock = $request->input('stock'); |
|
|
switch ($stock) { |
|
|
case 'in_stock': |
|
|
$query->where('Amount', '>', 5); |
|
|
break; |
|
|
case 'low_stock': |
|
|
$query->whereBetween('Amount', [1, 5]); |
|
|
break; |
|
|
case 'out_of_stock': |
|
|
$query->where('Amount', 0); |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if ($request->filled('search')) { |
|
|
$search = $request->input('search'); |
|
|
$query->where(function($q) use ($search) { |
|
|
$q->where('name', 'like', '%' . $search . '%') |
|
|
->orWhere('description', 'like', '%' . $search . '%'); |
|
|
}); |
|
|
} |
|
|
|
|
|
$products = $query->orderBy('created_at', 'desc')->get(); |
|
|
|
|
|
|
|
|
$customGames = CustomGame::orderBy('name')->get(); |
|
|
|
|
|
return view('table_product', compact('products', 'customGames')); |
|
|
} |
|
|
public function update(Request $request, $id) |
|
|
{ |
|
|
$request->validate([ |
|
|
'name' => 'required', |
|
|
'price' => 'required|numeric', |
|
|
'description' => 'required', |
|
|
'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048', |
|
|
'Amount' => 'required|numeric', |
|
|
'game' => 'nullable|string' |
|
|
]); |
|
|
|
|
|
$product = Product::findOrFail($id); |
|
|
$product->update($request->only(['name', 'price', 'description', 'Amount', 'game'])); |
|
|
|
|
|
if ($request->hasFile('image')) { |
|
|
|
|
|
if ($product->image && Storage::disk('public')->exists($product->image)) { |
|
|
Storage::disk('public')->delete($product->image); |
|
|
} |
|
|
|
|
|
$imagePath = $request->file('image')->store('products', 'public'); |
|
|
$product->image = $imagePath; |
|
|
$product->save(); |
|
|
} |
|
|
|
|
|
return redirect()->back()->with('success', 'Product updated successfully'); |
|
|
} |
|
|
|
|
|
public function destroy($id) |
|
|
{ |
|
|
try { |
|
|
$product = Product::findOrFail($id); |
|
|
|
|
|
|
|
|
if ($product->image && Storage::disk('public')->exists($product->image)) { |
|
|
Storage::disk('public')->delete($product->image); |
|
|
} |
|
|
|
|
|
$product->delete(); |
|
|
|
|
|
return back()->with('success', 'Product deleted successfully'); |
|
|
} catch (\Exception $e) { |
|
|
return back()->with('error', 'Failed to delete product: ' . $e->getMessage()); |
|
|
} |
|
|
} |
|
|
|
|
|
public function categories() |
|
|
{ |
|
|
|
|
|
$categoryCounts = [ |
|
|
'Genshin' => Product::where('game', 'Genshin')->count(), |
|
|
'Starrail' => Product::where('game', 'Starrail')->count(), |
|
|
'WutheringWave' => Product::where('game', 'WutheringWave')->count(), |
|
|
]; |
|
|
|
|
|
|
|
|
$customGames = CustomGame::orderBy('name')->get(); |
|
|
|
|
|
return view('categories', compact('categoryCounts', 'customGames')); |
|
|
} |
|
|
|
|
|
public function getByCategory($game) |
|
|
{ |
|
|
$products = Product::where('game', $game)->get(); |
|
|
|
|
|
|
|
|
$productsWithSales = $products->map(function ($product) { |
|
|
$salesCount = \App\Models\Order::where('cart_data', 'LIKE', '%"product_id":' . $product->id . '%') |
|
|
->where('status', 'completed') |
|
|
->count(); |
|
|
$product->sales_count = $salesCount; |
|
|
return $product; |
|
|
}); |
|
|
|
|
|
return response()->json($productsWithSales); |
|
|
} |
|
|
|
|
|
public function storeCustomGame(Request $request) |
|
|
{ |
|
|
try { |
|
|
$request->validate([ |
|
|
'name' => 'required|string|max:50|unique:custom_games,name', |
|
|
'icon' => 'nullable|string', |
|
|
'color_gradient' => 'nullable|string' |
|
|
]); |
|
|
|
|
|
$customGame = CustomGame::create([ |
|
|
'name' => $request->name, |
|
|
'icon' => $request->icon ?? 'fas fa-gamepad', |
|
|
'color_gradient' => $request->color_gradient ?? 'from-purple-500 to-blue-500' |
|
|
]); |
|
|
|
|
|
return response()->json([ |
|
|
'success' => true, |
|
|
'message' => "Custom game '{$request->name}' has been created successfully!", |
|
|
'data' => $customGame |
|
|
]); |
|
|
|
|
|
} catch (\Exception $e) { |
|
|
return response()->json([ |
|
|
'success' => false, |
|
|
'message' => 'Failed to create custom game: ' . $e->getMessage() |
|
|
], 500); |
|
|
} |
|
|
} |
|
|
|
|
|
public function deleteCustomGame($gameName) |
|
|
{ |
|
|
try { |
|
|
|
|
|
$productsCount = Product::where('game', $gameName)->count(); |
|
|
|
|
|
if ($productsCount > 0) { |
|
|
return response()->json([ |
|
|
'success' => false, |
|
|
'message' => "Cannot delete '{$gameName}' because {$productsCount} product(s) are still using this category. Please reassign or delete those products first." |
|
|
], 400); |
|
|
} |
|
|
|
|
|
|
|
|
$customGame = CustomGame::where('name', $gameName)->first(); |
|
|
if ($customGame) { |
|
|
$customGame->delete(); |
|
|
} |
|
|
|
|
|
return response()->json([ |
|
|
'success' => true, |
|
|
'message' => "Custom game '{$gameName}' has been deleted successfully!" |
|
|
]); |
|
|
|
|
|
} catch (\Exception $e) { |
|
|
return response()->json([ |
|
|
'success' => false, |
|
|
'message' => 'Failed to delete custom game: ' . $e->getMessage() |
|
|
], 500); |
|
|
} |
|
|
} |
|
|
|
|
|
public function search(Request $request) |
|
|
{ |
|
|
$query = $request->get('q', ''); |
|
|
|
|
|
if (strlen($query) < 2) { |
|
|
return response()->json(['games' => [], 'products' => []]); |
|
|
} |
|
|
|
|
|
|
|
|
$mainGames = [ |
|
|
[ |
|
|
'name' => 'Genshin Impact', |
|
|
'slug' => 'Genshin', |
|
|
'icon' => 'fas fa-star', |
|
|
'count' => Product::where('game', 'Genshin')->count(), |
|
|
'description' => 'Discover premium digital content for your favorite games', |
|
|
'gradient' => 'from-yellow-500 to-orange-500', |
|
|
'logo' => asset('images/games/genshin-logo.png'), |
|
|
'category' => 'RPG', |
|
|
'popularity' => 'Popular' |
|
|
], |
|
|
[ |
|
|
'name' => 'Honkai: Star Rail', |
|
|
'slug' => 'Starrail', |
|
|
'icon' => 'fas fa-rocket', |
|
|
'count' => Product::where('game', 'Starrail')->count(), |
|
|
'description' => 'Space fantasy RPG with strategic combat', |
|
|
'gradient' => 'from-purple-500 to-pink-500', |
|
|
'logo' => asset('images/games/starrail-logo.png'), |
|
|
'category' => 'RPG', |
|
|
'popularity' => 'Featured' |
|
|
], |
|
|
[ |
|
|
'name' => 'Wuthering Waves', |
|
|
'slug' => 'WutheringWave', |
|
|
'icon' => 'fas fa-wave-square', |
|
|
'count' => Product::where('game', 'WutheringWave')->count(), |
|
|
'description' => 'Open-world action RPG with stunning visuals', |
|
|
'gradient' => 'from-cyan-500 to-blue-500', |
|
|
'logo' => asset('images/games/wuthering-logo.png'), |
|
|
'category' => 'Action RPG', |
|
|
'popularity' => 'New' |
|
|
], |
|
|
[ |
|
|
'name' => 'Zenless Zone Zero', |
|
|
'slug' => 'ZenlessZoneZero', |
|
|
'icon' => 'fas fa-city', |
|
|
'count' => Product::where('game', 'ZenlessZoneZero')->count(), |
|
|
'description' => 'Urban fantasy action game', |
|
|
'gradient' => 'from-red-500 to-pink-500', |
|
|
'logo' => asset('images/games/zenless-logo.png'), |
|
|
'category' => 'Action', |
|
|
'popularity' => 'Trending' |
|
|
], |
|
|
[ |
|
|
'name' => 'Arknights', |
|
|
'slug' => 'Arknights', |
|
|
'icon' => 'fas fa-chess-knight', |
|
|
'count' => Product::where('game', 'Arknights')->count(), |
|
|
'description' => 'Strategic tower defense with anime characters', |
|
|
'gradient' => 'from-indigo-500 to-purple-500', |
|
|
'logo' => asset('images/games/arknights-logo.png'), |
|
|
'category' => 'Strategy', |
|
|
'popularity' => 'Classic' |
|
|
], |
|
|
[ |
|
|
'name' => 'Azur Lane', |
|
|
'slug' => 'AzurLane', |
|
|
'icon' => 'fas fa-ship', |
|
|
'count' => Product::where('game', 'AzurLane')->count(), |
|
|
'description' => 'Naval warfare with anthropomorphic ships', |
|
|
'gradient' => 'from-blue-500 to-teal-500', |
|
|
'logo' => asset('images/games/azurlane-logo.png'), |
|
|
'category' => 'Strategy', |
|
|
'popularity' => 'Popular' |
|
|
], |
|
|
]; |
|
|
|
|
|
|
|
|
$customGames = CustomGame::all()->map(function($game) { |
|
|
return [ |
|
|
'name' => $game->name, |
|
|
'slug' => $game->name, |
|
|
'icon' => $game->icon, |
|
|
'count' => Product::where('game', $game->name)->count(), |
|
|
'description' => 'Custom game category with unique items', |
|
|
'gradient' => $game->color_gradient, |
|
|
'logo' => asset('images/games/custom-logo.png'), |
|
|
'category' => 'Custom', |
|
|
'popularity' => 'Special' |
|
|
]; |
|
|
}); |
|
|
|
|
|
$allGames = collect($mainGames)->concat($customGames); |
|
|
|
|
|
|
|
|
$matchingGames = $allGames->filter(function($game) use ($query) { |
|
|
return stripos($game['name'], $query) !== false; |
|
|
})->take(5)->values(); |
|
|
|
|
|
|
|
|
$products = Product::where('name', 'like', "%{$query}%") |
|
|
->orWhere('description', 'like', "%{$query}%") |
|
|
->orWhere('game', 'like', "%{$query}%") |
|
|
->take(8) |
|
|
->get() |
|
|
->map(function($product) { |
|
|
|
|
|
$salesCount = \App\Models\Order::where('status', 'completed') |
|
|
->where('cart_data', 'LIKE', '%"id":' . $product->id . '%') |
|
|
->get() |
|
|
->sum(function($order) use ($product) { |
|
|
$cartData = json_decode($order->cart_data, true); |
|
|
$quantity = 0; |
|
|
if (is_array($cartData)) { |
|
|
foreach ($cartData as $item) { |
|
|
if (isset($item['id']) && $item['id'] == $product->id) { |
|
|
$quantity += $item['quantity'] ?? 1; |
|
|
} |
|
|
} |
|
|
} |
|
|
return $quantity; |
|
|
}); |
|
|
|
|
|
|
|
|
$rating = $salesCount > 0 ? min(5.0, 3.5 + ($salesCount / 100)) : 4.0; |
|
|
|
|
|
return [ |
|
|
'id' => $product->id, |
|
|
'name' => $product->name, |
|
|
'slug' => $product->slug ?? strtolower(str_replace(' ', '-', $product->name)), |
|
|
'game' => $product->game, |
|
|
'game_slug' => $product->game, |
|
|
'price' => number_format($product->price, 2), |
|
|
'original_price' => $product->price, |
|
|
'image' => $product->image ? asset($product->image) : 'https://via.placeholder.com/150x150/374151/ffffff?text=No+Image', |
|
|
'description' => $product->description, |
|
|
'short_description' => strlen($product->description) > 100 ? substr($product->description, 0, 100) . '...' : $product->description, |
|
|
'amount' => $product->Amount, |
|
|
'stock_status' => $product->Amount > 10 ? 'In Stock (' . $product->Amount . ')' : |
|
|
($product->Amount > 0 ? 'Low Stock (' . $product->Amount . ')' : 'Out of Stock'), |
|
|
'stock_color' => $product->Amount > 10 ? 'text-green-400' : |
|
|
($product->Amount > 0 ? 'text-yellow-400' : 'text-red-400'), |
|
|
'stock_bg' => $product->Amount > 10 ? 'bg-green-500/20' : |
|
|
($product->Amount > 0 ? 'bg-yellow-500/20' : 'bg-red-500/20'), |
|
|
'created_at' => $product->created_at->format('M d, Y'), |
|
|
'is_new' => $product->created_at->diffInDays(now()) <= 7, |
|
|
'rating' => round($rating, 1), |
|
|
'sales_count' => $salesCount, |
|
|
'total_revenue' => $salesCount * $product->price, |
|
|
'availability' => $product->Amount > 0 ? 'Available' : 'Sold Out', |
|
|
'category_icon' => $this->getGameIcon($product->game), |
|
|
'popularity_score' => $salesCount > 50 ? 'Hot' : ($salesCount > 20 ? 'Popular' : 'New') |
|
|
]; |
|
|
}); |
|
|
|
|
|
return response()->json([ |
|
|
'games' => $matchingGames, |
|
|
'products' => $products |
|
|
]); |
|
|
} |
|
|
|
|
|
private function getGameIcon($game) |
|
|
{ |
|
|
$icons = [ |
|
|
'Genshin' => 'fas fa-star', |
|
|
'Starrail' => 'fas fa-rocket', |
|
|
'WutheringWave' => 'fas fa-wave-square', |
|
|
'ZenlessZoneZero' => 'fas fa-city', |
|
|
'Arknights' => 'fas fa-chess-knight', |
|
|
'AzurLane' => 'fas fa-ship', |
|
|
]; |
|
|
|
|
|
return $icons[$game] ?? 'fas fa-gamepad'; |
|
|
} |
|
|
} |
|
|
|