Files
hub/app/Http/Controllers/MarketplaceController.php
kelly c7d6ee5e21 feat: multiple UI/UX improvements and case-insensitive search
- Refactor New Quote page to enterprise data-entry layout (2-column, dense)
- Add Payment Terms dropdown (COD, NET 15, NET 30, NET 60)
- Fix sidebar menu active states and route names
- Fix brand filter badge visibility on brands page
- Remove company_name references (use business instead)
- Polish Promotions page layout
- Fix double-click issue on sidebar menu collapse
- Make all searches case-insensitive (like -> ilike for PostgreSQL)
2025-12-14 15:36:00 -07:00

204 lines
6.0 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Brand;
use App\Models\Product;
use App\Models\Strain;
use Illuminate\Http\Request;
class MarketplaceController extends Controller
{
/**
* Display marketplace browse page
*/
public function index(Request $request)
{
// Start with active products only
$query = Product::query()
->with(['brand', 'strain'])
->active();
// Search filter (name, SKU, description)
if ($search = $request->input('search')) {
$query->where(function ($q) use ($search) {
$q->where('name', 'ilike', "%{$search}%")
->orWhere('sku', 'ilike', "%{$search}%")
->orWhere('description', 'ilike', "%{$search}%");
});
}
// Brand filter
if ($brandId = $request->input('brand_id')) {
$query->where('brand_id', $brandId);
}
// Strain type filter
if ($strainType = $request->input('strain_type')) {
$query->whereHas('strain', function ($q) use ($strainType) {
$q->where('type', $strainType);
});
}
// Price range filter
if ($priceMin = $request->input('price_min')) {
$query->where('wholesale_price', '>=', $priceMin);
}
if ($priceMax = $request->input('price_max')) {
$query->where('wholesale_price', '<=', $priceMax);
}
// In stock filter
if ($request->input('in_stock')) {
$query->where('quantity_on_hand', '>', 0);
}
// Sorting
$sort = $request->input('sort', 'newest');
match ($sort) {
'name_asc' => $query->orderBy('name', 'asc'),
'name_desc' => $query->orderBy('name', 'desc'),
'price_asc' => $query->orderBy('wholesale_price', 'asc'),
'price_desc' => $query->orderBy('wholesale_price', 'desc'),
'newest' => $query->latest(),
default => $query->latest(),
};
// Paginate results
$products = $query->paginate(12)->withQueryString();
// Get all active brands for filters
$brands = Brand::active()->orderBy('name')->get();
// Get featured products for carousel (exclude from main results if in first page)
$featuredProducts = Product::query()
->with(['brand', 'strain'])
->featured()
->inStock()
->limit(3)
->get();
$business = auth()->user()->businesses->first();
return view('buyer.marketplace.index', compact('products', 'brands', 'featuredProducts', 'business'));
}
/**
* Display all products (redirects to main browse page)
*/
public function products()
{
return redirect()->route('buyer.browse');
}
/**
* Display all brands directory
*/
public function brands()
{
$brands = Brand::query()
->active()
->withCount(['products' => function ($query) {
$query->active();
}])
->orderBy('name')
->get();
$business = auth()->user()->businesses->first();
return view('buyer.marketplace.brands', compact('brands', 'business'));
}
/**
* Display products in specific category
*/
public function category($category)
{
$business = auth()->user()->businesses->first();
return view('buyer.marketplace.category', compact('category', 'business'));
}
/**
* Show individual product (nested under brand)
*/
public function showProduct($brandSlug, $productSlug)
{
// Find brand by slug
$brand = Brand::query()
->where('slug', $brandSlug)
->active()
->firstOrFail();
// Find product by slug within this brand
$product = Product::query()
->with([
'brand',
'strain',
'availableBatches' => function ($query) {
$query->with(['coaFiles'])
->orderBy('production_date', 'desc')
->orderBy('created_at', 'desc');
},
])
->where('brand_id', $brand->id)
->where(function ($query) use ($productSlug) {
$query->where('slug', $productSlug);
// Only try ID lookup if the value is numeric
if (is_numeric($productSlug)) {
$query->orWhere('id', $productSlug);
}
})
->active()
->firstOrFail();
// Get related products from same brand
$relatedProducts = Product::query()
->with(['brand', 'strain'])
->where('brand_id', $product->brand_id)
->where('id', '!=', $product->id)
->active()
->inStock()
->limit(4)
->get();
$business = auth()->user()->businesses->first();
return view('buyer.marketplace.product', compact('product', 'relatedProducts', 'brand', 'business'));
}
/**
* Show individual brand storefront
*/
public function showBrand($brandSlug)
{
// Find brand by slug
$brand = Brand::query()
->where('slug', $brandSlug)
->active()
->firstOrFail();
// Get featured products from this brand
$featuredProducts = Product::query()
->with(['strain'])
->where('brand_id', $brand->id)
->featured()
->inStock()
->limit(3)
->get();
// Get all products from this brand
$products = Product::query()
->with(['strain'])
->where('brand_id', $brand->id)
->active()
->orderBy('is_featured', 'desc')
->orderBy('name')
->paginate(20);
$business = auth()->user()->businesses->first();
return view('buyer.marketplace.brand', compact('brand', 'featuredProducts', 'products', 'business'));
}
}