192 lines
5.8 KiB
PHP
192 lines
5.8 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Home\Product;
|
|
|
|
use App\Models\Bottle;
|
|
use App\Models\Category;
|
|
use App\Models\Perfume;
|
|
use App\Models\Product;
|
|
use Illuminate\Contracts\View\View;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\Url;
|
|
use Livewire\Component;
|
|
use Livewire\WithoutUrlPagination;
|
|
use Livewire\WithPagination;
|
|
|
|
#[Layout('components.layouts.home', [
|
|
'title' => 'Produk',
|
|
])]
|
|
class Index extends Component
|
|
{
|
|
use WithoutUrlPagination, WithPagination;
|
|
|
|
public ?string $search = null;
|
|
|
|
public ?string $sort = 'latest';
|
|
|
|
#[Url(as: 'type')]
|
|
public string $productType = 'perfume';
|
|
|
|
#[Url(as: 'categories')]
|
|
public array $selectedCategories = [];
|
|
|
|
#[Url(as: 'price')]
|
|
public ?string $priceRange = null;
|
|
|
|
public array $availableCategories = [];
|
|
|
|
public array $recommendations = [];
|
|
|
|
public function mount(): void
|
|
{
|
|
$this->availableCategories = Category::perfume()
|
|
->orderBy('sort_order')
|
|
->pluck('name', 'id')
|
|
->toArray();
|
|
|
|
$this->recommendations = Perfume::with(['brand', 'categories', 'items'])
|
|
->withCount('items')
|
|
->orderBy('items_count', 'desc')
|
|
->take(8)
|
|
->get()
|
|
->map(function (Perfume $perfume) {
|
|
$perfume->image = $perfume->getFirstMediaUrl('image') ?: asset('assets/images/dark-logo.png');
|
|
|
|
return $perfume;
|
|
})
|
|
->toArray();
|
|
}
|
|
|
|
public function updatedSearch(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function updatedSort(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function updatedProductType(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function updatedSelectedCategories(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function updatedPriceRange(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function render(): View
|
|
{
|
|
// Determine which model to query based on productType
|
|
$query = match ($this->productType) {
|
|
'bottle' => Bottle::query(),
|
|
'arabian' => Product::query(),
|
|
default => Perfume::with(['brand', 'categories']), // 'perfume'
|
|
};
|
|
|
|
// Load items count for badge calculation
|
|
$query->withCount([
|
|
'items as total_sales' => function ($q) {
|
|
$q->whereHas('order', function ($orderQuery) {
|
|
$orderQuery->where('created_at', '>=', now()->subMonth());
|
|
});
|
|
},
|
|
]);
|
|
|
|
// Apply search filter
|
|
$query->when($this->search, function (Builder $q) {
|
|
$q->where('name', 'like', '%'.$this->search.'%');
|
|
});
|
|
|
|
// Apply category filter (only for perfumes)
|
|
if ($this->productType === 'perfume' && $this->selectedCategories) {
|
|
$query->whereHas('categories', function (Builder $q) {
|
|
$q->whereIn('name', $this->selectedCategories);
|
|
});
|
|
}
|
|
|
|
// Apply price range filter
|
|
$query->when($this->priceRange, function (Builder $q) {
|
|
match ($this->priceRange) {
|
|
'< 100k' => $q->where('sale_price', '<', 100000),
|
|
'100k - 500k' => $q->whereBetween('sale_price', [100000, 500000]),
|
|
'500k - 1jt' => $q->whereBetween('sale_price', [500000, 1000000]),
|
|
'> 1jt' => $q->where('sale_price', '>', 1000000),
|
|
default => null,
|
|
};
|
|
});
|
|
|
|
// Apply sorting
|
|
$query->when($this->sort, function (Builder $q) {
|
|
match ($this->sort) {
|
|
'price_low' => $q->orderBy('sale_price', 'asc'),
|
|
'price_high' => $q->orderBy('sale_price', 'desc'),
|
|
'popular' => $q->withCount('items')->orderBy('items_count', 'desc'),
|
|
default => $q->latest(), // 'latest'
|
|
};
|
|
}, fn (Builder $q) => $q->latest());
|
|
|
|
// Paginate and transform results
|
|
$products = $query->paginate(9)->through(function ($product) {
|
|
$product->image = $product->getFirstMediaUrl('image') ?: asset('assets/images/dark-logo.png');
|
|
|
|
// Add categories for perfumes if not already loaded
|
|
if ($this->productType === 'perfume' && ! $product->relationLoaded('categories')) {
|
|
$product->load('categories');
|
|
}
|
|
|
|
// Determine badge with priority
|
|
$product->badge = $this->determineBadge($product);
|
|
|
|
return $product;
|
|
});
|
|
|
|
return view('livewire.home.product.index', [
|
|
'pageTitle' => 'Produk',
|
|
'pageDesc' => 'Temukan aroma yang mencerminkan kepribadianmu! 🌸✨ Dari kemewahan parfum sampai botol eksklusif, semua ada di sini.',
|
|
'products' => $products,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Determine badge for product based on priority
|
|
* Priority: 1. Baru, 2. Terlaris, 3. Hot
|
|
*/
|
|
private function determineBadge($product): ?array
|
|
{
|
|
// Priority 1: Terlaris (sales >= 500 in last month)
|
|
if (isset($product->total_sales) && $product->total_sales >= 500) {
|
|
return [
|
|
'label' => 'Terlaris',
|
|
'class' => 'bg-red-500 text-white',
|
|
];
|
|
}
|
|
|
|
// Priority 2: Baru (created within last month)
|
|
if ($product->created_at && $product->created_at->isAfter(now()->subMonth())) {
|
|
return [
|
|
'label' => 'Baru',
|
|
'class' => 'bg-home-primary text-white',
|
|
];
|
|
}
|
|
|
|
// Priority 3: Hot (high views - top 20% or > 100 views)
|
|
if (isset($product->views) && $product->views > 100) {
|
|
return [
|
|
'label' => 'Hot',
|
|
'class' => 'bg-orange-500 text-white',
|
|
];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|