Di langkah ini, kita akan berfokus pada Halaman Depan E-Commerce (Homepage) untuk menampilkan produk kepada pengunjung.
Langkah 1: Route & Controller untuk Halaman Depan
Buat route untuk homepage di routes/web.php. Karena ini halaman depan, biasanya kita letakkan di root /:
use App\Http\Controllers\FrontendController;
Route::get('/', [FrontendController::class, 'index'])->name('home');
Buat controller FrontendController jika belum ada:
php artisan make:controller FrontendController
Di app/Http/Controllers/FrontendController.php isi sebagai berikut:
<?php
namespace App\Http\Controllers;
use App\Models\Product;
use App\Models\Category;
use Illuminate\Http\Request;
use Inertia\Inertia;
class FrontendController extends Controller
{
public function index(Request $request)
{
// Ambil parameter search & filter kategori
$search = $request->input('search');
$categoryId = $request->input('category_id');
$query = Product::with('images', 'category');
if ($search) {
$query->where('name', 'like', '%' . $search . '%');
}
if ($categoryId) {
$query->where('category_id', $categoryId);
}
$products = $query->latest()->paginate(12)->withQueryString();
$categories = Category::all(['id','name']);
return Inertia::render('Frontend/Homepage', [
'products' => $products,
'categories' => $categories,
'filters' => [
'search' => $search,
'category_id' => $categoryId,
]
]);
}
}
Penjelasan:
- Kita mengambil
$searchdan$categoryIddari query string untuk fitur pencarian dan filter kategori. - Query produk di-filter sesuai parameter tersebut.
withQueryString()agar pagination mempertahankan parameter search & category saat berganti halaman.- Mengirim
categoriesuntuk menampilkan dropdown kategori. - Mengirim
filtersuntuk mempertahankan nilai form search dan dropdown filter di frontend.
Langkah 2: Komponen Frontend (Vue.js - Homepage)
Buat file resources/js/Pages/Frontend/Homepage.vue:
<template>
<div class="min-h-screen flex flex-col">
<!-- NAVBAR -->
<header class="bg-white shadow z-10">
<div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
<div class="text-2xl font-bold text-gray-800">MyShop</div>
<nav class="flex items-center space-x-6">
<Link href="/" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">
Home
</Link>
<Link href="/categories" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">
Categories
</Link>
<Link href="/about" class="text-gray-700 hover:text-gray-900 font-medium px-3 py-2 transition-colors">
About
</Link>
</nav>
</div>
</header>
<!-- HERO SECTION -->
<section
class="relative bg-cover bg-center flex items-center justify-center text-center"
style="background-image: url('https://images.unsplash.com/photo-1542831371-29b0f74f9713?ixlib=rb-4.0.3&q=80&fm=jpg&crop=entropy&w=1200'); height: 60vh;"
>
<div class="bg-black bg-opacity-50 w-full h-full absolute top-0 left-0"></div>
<div class="relative z-10 max-w-2xl mx-auto px-4 text-white">
<h1 class="text-4xl md:text-5xl font-bold mb-4">Welcome to MyShop</h1>
<p class="text-lg md:text-xl mb-6">Find the best products for your daily life</p>
<form @submit.prevent="applyFilter" class="flex items-center max-w-md mx-auto">
<input
v-model="form.search"
type="text"
placeholder="Search products..."
class="border rounded-l px-3 py-2 w-full"
/>
<button type="submit" class="px-4 py-2 bg-blue-600 text-white rounded-r hover:bg-blue-700">Search</button>
</form>
</div>
</section>
<!-- MAIN CONTENT -->
<main class="flex-1 bg-gray-100 py-10">
<div class="max-w-7xl mx-auto px-4">
<!-- FILTER BAR -->
<div class="flex items-center justify-between mb-6">
<div class="flex space-x-4 items-center">
<label class="block font-medium text-gray-700">Category:</label>
<select v-model="form.category_id" @change="applyFilter" class="border px-3 py-2 rounded bg-white">
<option value="">All</option>
<option v-for="cat in categories" :key="cat.id" :value="cat.id">{{ cat.name }}</option>
</select>
</div>
</div>
<!-- PRODUCT GRID -->
<div class="grid grid-cols-2 md:grid-cols-4 gap-6">
<div
v-for="product in products.data"
:key="product.id"
class="bg-white p-4 rounded shadow hover:shadow-md transition-shadow"
>
<Link :href="route('products.show', product.slug)" class="block relative pb-1/1 overflow-hidden rounded">
<img
v-if="product.images && product.images.length > 0"
:src="`/storage/${product.images[0].image}`"
alt="Product Image"
class="w-full h-full object-cover rounded"
/>
<div v-else class="absolute h-full w-full flex items-center justify-center text-gray-400">
No Image
</div>
</Link>
<Link :href="route('products.show', product.slug)">
<h2 class="mt-2 font-semibold text-sm text-gray-800 line-clamp-2 hover:text-blue-600 transition-colors">{{ product.name }}</h2>
</Link>
<p class="text-xs text-gray-500">{{ product.category ? product.category.name : 'No Category' }}</p>
<p class="text-red-600 font-bold mt-1 text-sm">Rp {{ product.price }}</p>
</div>
</div>
<!-- PAGINATION -->
<div class="mt-8 flex justify-center">
<div class="inline-flex space-x-2">
<div v-for="link in products.links" :key="link.url">
<Link
:href="link.url"
v-html="link.label"
:class="[
'px-3 py-1 border rounded text-sm',
link.active ? 'bg-blue-600 text-white border-blue-600' : 'bg-white text-gray-700 hover:bg-gray-100 border-gray-300'
]"
></Link>
</div>
</div>
</div>
</div>
</main>
<!-- FOOTER -->
<footer class="bg-white py-4">
<div class="max-w-7xl mx-auto px-4 text-center text-gray-600 text-sm">
© 2023 MyShop. All rights reserved.
</div>
</footer>
</div>
</template>
<script>
import { Link, useForm } from '@inertiajs/inertia-vue3';
export default {
props: {
products: Object,
categories: Array,
filters: Object
},
components: { Link },
setup(props) {
const form = useForm({
search: props.filters.search || '',
category_id: props.filters.category_id || ''
});
const applyFilter = () => {
form.get('/', { preserveScroll: true, replace: true });
};
return { form, applyFilter };
},
};
</script>
<style>
.pb-1/1 {
padding-bottom: 100%;
}
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
</style>
Langkah 3: Testing Pencarian & Filter
Buka http://localhost:8000 Jika berhasil, akan seperti berikut:
Selamat Mencoba!!!. Silahkan tinggalkan komentar jika belum berhasil.